-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pre-RFC: std
aware Cargo
#1
Conversation
CC: @japaric @alexcrichton @yoshuawuyts I'll plan on posting this to i/u.r-l.o once I have it 100% done, but the bulk of it is ready for review, and I would welcome comments! |
Note to self: Probably need to clarify items around precompiled versions of |
Overall I found this quite approachable, and the RFC makes sense to me. Thanks for writing this @jamesmunns! |
Thanks for this! Some thoughts I had while reading:
|
Hey @alexcrichton, here are a couple notes (some answers, some open questions), If you don't have any further comments, this is how I will address the items you mentioned:
Currently I've written that core/std would be rebuilt when a .json target is provided, but that isn't quite good enough. I think any of the following would trigger cargo to rebuild core/std (and if none of these are true, a precompiled version is used):
Yup, this is an open question. I would say using rustup on day one is fine, and making the "cargo should lazily but automatically get its dependencies" should be a separate RFC.
I'm afraid I don't understand this. Is there something like a stage0 core used to build core?
Ah, this is a good point. I'm not sure how this will work if the user tries to patch certain dependencies that are shared with core/std. I'd say in the most conservative mode, we can say that these dependencies are just part of core/std, and cannot be touched. Maybe add the ability to patch core/std deps as a nightly feature in the future?
I think I touched on this above, but you're right, I do think we need a "don't use core" for
Hmm, I do think this could help, though if you allow for profile modifications to trigger a rebuild, you might end up with a lot of |
Ok cool that sounds pretty good to me. For profile settings though I think we'll need to find a good middle ground because if you tweak profile settings today it doesn't already recompile libstd/libcore, and it may not always be desired that they're recompiled. For some use cases, however, you definitely do want to recompile! I think we'd definitely recompile std if core is patched!
Ok cool that makes sense to me. The answer I think is then "Cargo looks for a special location in the rustc sysroot and then errors out if it isn't there" and presumably Cargo could provide a nice-ish error message talking about rustup.
Yeah I think patching is definitely gonna happen later in the future. Now that I think about this we may just need to be more principled about the crates.io dependencies for libstd on crates.io, accurately doing major version bumps and such. In any case, something to worry about later!
I don't think there's actually much to reuse, but one possible option is that by default crates depend on std/core. If you mention anything, though, then the implicit dependencies are broken. For example this crate depends on std/core default features: [dependencies] whereas this crate only depends on core: [dependencies]
core = { sysroot = true } (or something like that) I'm not sure if this is the best design though, and seems like something good to discuss on the RFC!
Heh true! We have a lot of binaries already though :) |
Yeah, for the profile items, we might want a new profile flag like "apply-to-sysroot", which is false by default, but can be opted in to, forcing the rebuild of std. This would look like: [profile.release]
opt-level = 'z'
apply-to-sysroot = true For the source stuff, I think looking in the right place in sysroot, and erroring with a "you need to provide the source to rebuild sysroot. Install using rustup with I think the rest of the items are good to discuss in the RFC, I'll make sure they all make it into the open questions section :) Feel free to tag anyone else you think would have good input on this, I'll be plugging it at the next Embedded-WG meeting, and I hope to have this ready to share (as a pre-rfc) more publicly (on u/i.r-l.o) by the middle of this week. @alexcrichton should this be a |
All sounds good to me! We don't have a ton of precedent of using rust-lang/cargo for Cargo RFCs, so let's probably stick to rust-lang/rfcs for now |
@jamesmunns Thanks for writing this! It looks good to me. 🚀
(I wonder if we'll want some tamper protection mechanism for the rust-src component. It'd be easy to modify the source on disk to e.g. wrap unstable API in stable API.) |
text/0000-std-aware-cargo.md
Outdated
|
||
* Supporting new/arbitrary targets, such as those defined by a ".json" file | ||
* Making modifications to `core` or `std` through use of feature flags | ||
* Users who would like to make different optimizations to `core` or `std`, such as `opt-level = 'z'`, with `panic = "abort"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd add that we'd really like to be able to have debug_assertions in core
, alloc
, and std
. See threads like https://internals.rust-lang.org/t/make-vec-set-len-enforce-the-len-cap-invariant/8927?u=scottmcm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @scottmcm, is this something you would see as a compile time configuration option of core
, std
, alloc
, etc?
This RFC isn't meant to propose which features should or should not be available, but instead is focused on the method for selecting them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jamesmunns Yes, I do see it as a compile-time configuration option. (Ditto for having overflow checks on by default in debug but not in release -- right now that's hacked together with #[rustc_inherit_overflow_checks]
, which I'd love to be able to remove.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I'm fine with the RFC not saying how those particular things should be accomplished, but I think they're valuable examples since it's the Motivation section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scottmcm Would you be interested in PR-ing (or suggesting text) for a section in "Future possibilities" to discuss a listing of possible flags to stabilize? I don't think this should impact the RFC, but might be good to centralize a list of things people are thinking of.
In general, I want to make it clear in that section that "this is a list of possible flags to stabilize, but this RFC does not guarantee any of the following will be stabilized".
Feel free to fork my repo and submit a PR to this branch, and I can accept it there.
[summary]: #summary | ||
|
||
Currently, the `core` and `std` components of Rust are handled in a different way than Cargo handles other crate dependencies. This causes issues for non-mainstream targets, such as WASM, Embedded, and new not-yet-tier-1 targets. The following RFC proposes a roadmap to address these concerns in a consistent and incremental process. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to give a brief summary of the "how it is achieved" here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was a little bit worried about repeating myself as this is explained without detail (in the Guide Level), and explained with detail (in the Reference Level).
I am unsure how to summarize without repeating the content of the Guide Level Explanation verbatim. I am open to suggestions!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to think of something to suggest.. :)
The stabilization of a `core` feature flag would require a process similar to the stabilization of a feature in the language: | ||
|
||
* Any new feature begins as unstable, requiring a nightly compiler | ||
* When the feature is sufficiently explored, an RFC/PR can be made to `libcore` to promote this feature to stable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we would add new feature flags to Cargo.toml
of libcore? You may want to clarify this with less familiar readers...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I hadn't actually considered that far. I would suppose that would be one possible way of implementation.
|
||
#### Implementation of Stable Features | ||
|
||
There would be some mechanism of differentiating between flags used to build core, sorting them into the groups `unstable` and `stable`. This RFC does not prescribe a certain way of implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair; but you may want to suggest a few possible mechanisms to make this implementable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps! But I am not an active developer of core
or std
, so I was trying to avoid something that makes sense to me, but doesn't make sense in practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair :) Maybe someone else can help out here? e.g. @alexcrichton?
text/0000-std-aware-cargo.md
Outdated
[prior-art]: #prior-art | ||
|
||
* https://github.com/rust-lang/rfcs/pull/1133 | ||
* https://github.com/japaric/xargo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to elaborate on these links and give a summary :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rust-lang/cargo#2768 you might also want to link and also rust-lang/cargo#5002 and rust-lang/cargo#5003
|
||
## Should Cargo obtain or verify the source code for `libcore` or `libstd`? | ||
|
||
Right now we depend on `rustup` to obtain the correct source code for these libraries, and we rely on the user not to tamper with the contents. Are these reasonable decisions? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discuss possible ways to avoid tampering? If we rely on users not tampering, how do we communicate this effectively?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the first thing we should decide is IF we should try to detect tampering, and whether that makes sense when the user owns their own PC anyway.
I am of the opinion that we shouldn't (as I think it is a cat and mouse game), however this was brought up by multiple people :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the first thing we should decide is IF we should try to detect tampering, and whether that makes sense when the user owns their own PC anyway.
Right, but we should at least be sure that we can detect tampering.. ;) IOW, let's not decide we want it and find out later that we cannot.
I am of the opinion that we shouldn't (as I think it is a cat and mouse game), however this was brought up by multiple people :)
Things like people wanting to use RUSTC_BOOTSTRAP
on stable make me less sure; but maybe we can use social instead of technical means to discourage tampering?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO: Changing libcore/libstd would only affect the binaries produced by building this project, and would not be easy to "accidentally" end up with (except as a consumer of that binary). At the end of the day, anyone can patch the rust compiler for their own builds and distributions, which would defeat any measures we put in place.
Even if we bake in the CRCs of the source files into rustc
, people can patch the rustc
binary, or rebuild their own compiler. At the end of the day, the compiler is code running on their computer, which we can't do much about.
At best, I think tamper detection would serve only as a warning, "you changed the source, this is not supported behavior".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At best, I think tamper detection would serve only as a warning, "you changed the source, this is not supported behavior".
If it's not too troublesome performance and implementation-wise, that seems like a decent solution; at least we have communicated what we do and do not consider stable in a direct way then. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! I am just trying to avoid wasted engineering time, and even just baking the CRC of the total source into the compiler seems like it may be more trouble than it is worth. Let's see what other people's feedback is!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah fair; for now you could discuss this as a possibility :)
|
||
By using stable feature flags for `std`, we could say that `std` as a crate with `default-features = false` would essentially be `no_core`, or with `features = ["core"]`, we would be the same as `no_std`. | ||
|
||
This abstraction may not map to the actual implementation of `libcore` or `libstd`, but instead be an abstraction layer for the end developer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go into how we could architect such an abstraction layer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps I was unclear, but the "abstraction layer" is that everything except std
goes away from the user's perspective, and today's no_core
and no_std
are just std
with no/fewer features set.
I call it an abstraction layer, as under the hood it is unlikely (or undesirable) to try and merge libcore
, liballoc
, libstd
, et. al due to a number of reasons (discussed by the libs team at all-hands).
Let me know how much of that you would like me to add to the RFC or expand upon :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know how much of that you would like me to add to the RFC or expand upon :)
Mostly, I think it would be good to consider how we might architect the facade that core
and alloc
would presumably turn into... possibly with some code snippets as examples.
Co-Authored-By: jamesmunns <james@onevariable.com>
Co-Authored-By: jamesmunns <james@onevariable.com>
Suggestion cleanups Co-Authored-By: jamesmunns <james@onevariable.com>
"Currently" will slip over time. Perhaps pin to a specific version? "As of Rust 1.34, ..." |
Sorry to bikeshed, but I guess I might as well ask the question. Is JSON already deeply baked in? It has always struck me as a bit strange to use format for configuration, when Cargo uses TOML. |
text/0000-std-aware-cargo.md
Outdated
|
||
#### Stabilization of JSON target format | ||
|
||
As the custom target json files would become part of the stable interface of Cargo. The format used by this JSON file must become stabilized, and further changes must be made in a backwards compatible way to guarantee stability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always wondered why JSON is used for target specifications. I assume for historical reasons?
I know that it would be more work, but how about switching to TOML? It would be consistent with the other configuration files used by Rust (Cargo.toml, Cargo.lock, .cargo/config) and has the big advantage that it supports comments, which seems very useful because some target options are anything but self-documenting. It would also allow grouping the options in sections, e.g. for separating architecture options (data-layout etc) from build options (linker-flavor etc) if we like.
cc @Ericson2314, who created rust-lang#1133 |
Thanks @phil-opp, I'll try to get to this soon. |
Regarding TOML vs JSON syntax (CC @timClicks and @phil-opp), I would probably agree with you, though I'd prefer not to tack that on to this RFC. I think since this only touches components that haven't been stabilized, it might be easier to submit an RFC addressing changing JSON => TOML (extending/replacing RFC0131 ), separately from this RFC. |
Also thank you @phil-opp, I was looking for that RFC, and failed to find it! I'll plan on reviewing it, and see if I need to make any modifications to my RFC, or at least address any open concerns. @Ericson2314 it would be great to have any feedback from your perspective as well :) |
@jamesmunns Yeah everything looks good, and the last paragraph confirmed you've got the right narrative in your head :). To reiterate to everyone else, the "lodestar" that's best to keep in mind is that eventually we want standard libraries to become as unspecial as special. Specifically the sysroot should indeed eventually go away as we instead have a generic mechanism for caching (and distributing prebuilt) binaries. Likewise it's important than old crates that don't list their stdlib dependencies work as if they do. In the middle term, yes, we may not able to hit all these goals at once, but in the super close term, I think we should in fact not worry about any intermediate steps either. Specifically, we can punt on worrying about distributing std sources and working with the sysroot by focusing on two specific use-cases:
To my delight, the RFC as totally on board with all that, with the JSON target script being an elegant trick to opt-out of normal sysroot stuff absent anything else, and the I linked it above, but my old PR rust-lang/cargo#2768 that aimed to address just those two use-cases, contra my RFC, may be of some use in fleshing out the details. (Thought it could just be too bitrotted to help!) |
To the concerns about lockfiles, I would again punt on this as only a matter of sysroots and caching. But looking ahead, the general idea is twofold:
|
Oh, what's the point of I ask because I think this is the one exception in the RFC to not baking "sysroot" into any exposed interfaces. |
Terrible idea: It might be cute to allow adding |
I love it! |
Along with the previously-mentioned inconsistency of using JSON, I've always found the target specification format pretty arcane, and not very documented (as someone who's used them a fair amount). There are also a few options that are very LLVM-specific ( I think we should at least review and document the current situation before stabilisation (preferably separately, so as to not detract from how awesome getting this working would be). Apart from that, I'm very excited to see this make progress and the overall plan is what I've been hoping for for a long while! 🎉 |
I think Cargo could rely on there being a target file that Rustc understands without making it's contents stable, no? That's all that's needed to switch on/off the sysroot stuff, not actually inspection. |
The other option, of course, would be to at first only support custom target specifications on nightly (and not stabilise any of that stuff yet), and only let people build custom sysroot crates for targets that don't currently provide official builds (all the Tier 3 targets, if I'm not mistaken) on stable. Custom targets could then be stabilised separately, at a later date. |
These seem like very important considerations before the spec could be stablized. Could this RFC potentially proceed with a statement like "currently-accepted JSON or whatever standard later established"? Perhaps an idea would be to mandate necessary attributes, but not the specific format that they're encoded in? |
|
||
In some cases, it may be desirable to modify `core` in set of predefined manners. For example, on some targets it may be necessary to have lighter weight machinery for `fmt`. | ||
|
||
This step would provide a path for stabilization of compile time `core` features, which would be a subset of all total compile time features of `core`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While not essential, I have an additional use case that fits with Rust's larger goals: reducing the complexity for the beginner.
To someone new to Rust, requiring the "nightly compiler" to embark on embedded development can feel unsettling. Nightly feels advanced & dangerous, stable feels safer and more secure. ("I thought embedded was a great fit for Rust, why can't the stable compiler version handle that yet?") It also increases the teaching complexity, as I've encountered writing some drafts of Rust in Action content.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the thing. A lot the reason we don't just stabilize everything needed for low-level is it's more complex than it needs to be. There's so many things that are just....needlessly different between "regular" and embedded development. The needless barriers between standard libraries and regular libraries are just one example of this.
The switch to unstable Rustc feels spooky, but ultimately just means that more things are available. Everything that works with stable Rust also works with unstable Rust. It's more spooky than actually dangerous.
If you students ask, tell them it's so future students get a smoother experience and we aren't stuck in a situation that cannot improve like Clang/GCC and C/C++.
The RFC mentions in a couple of places that things are meant to be configured in the "root" crate. This is not how normal feature resolution works in Cargo. What would happen if a dependency specifies a dependency on core/std and sets features? Are you intending there to be special logic for core/std? I agree with @Ericson2314 that things should be as un-special as possible. We need a general mechanism to make global decisions (rust-lang-nursery/portability-wg#3 / rust-lang#2492). Not sure if this next question needs to be resolved in the RFC, but I figured I'd bring it to your attention. When patching the sysroot, how would you like to handle
|
|
||
### Caveats | ||
|
||
As stability guarantees cannot be made around modified versions of `core` or `std`, a nightly compiler must always be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why replacing core
or std
with a custom version requires nightly? How is it different from no_std
and cargo source replacement which are both available on stable and allow similar effects? It makes core
and std
special again because the user has the freedom to use custom source for any other crate but for some reason is not allowed to use custom source for core
or std
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me that just replacing core
or std
with a custom version should be fine on stable, those custom versions should not be allowed to bypass stability like the standard version, so they will almost certainly require nightly to compile, but that's separate from the act of replacement.
3. Extend the new behaviors described in step 1 and 2 for `std` (and `alloc`). | ||
4. Allow the user to provide their own custom source versions of `core` and `std`, allowing for deep customizations when necessary. This will require a nightly version of the compiler. | ||
|
||
As a new concept, the items above propose the existence of "stable features" for `core` and `std`. These features would be considered stable with the same degree of guarantees made for stability in the rest of the language. These features would allow configuration of certain functionalities of `core` or `std`, in a way decided at compile time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How strong would the testing guarantees of stable features be? Do stable features mean a commitment to testing every combination of stable features on every tier-1 platform? If so, then that blows up the cost of CI by a factor of 2n.
@japaric yeah |
Echoing some comments on specific changes, I question why a nightly compiler is always going to be needed. Once 'std'-aware Cargo and Rust are stable, surely one shouldn't then need a Nightly compiler to use feature flags? As stable as Nightly might be, it's still not the stable version. |
Okay, here are a couple notes and responses, based on topics. Misc note to @Ericson2314: The use of a target file is not the only way cargo could decide to recompile Misc note to @granthusbands: A nightly compiler is only required in some cases, which is discussed in most use case areas of the detailed design. With regards to feature flags, currently none of the feature flags in std or core have been committed as stable, because setting them requires a nightly compiler. This RFC proposes a way to mark feature flags as stable, which would allow them to be set or not set by a stable compiler. @Ericson2314's previous efforts
Crate Root Dependency
Sysroot
Lockfile
Defining non-dependencies on
|
Ah thanks for reminding me how With that in mind, I do not think standard library crates should be separately namespaced. I do like the std facade approach, which means creating more creates behind std (along with using existing ones like |
I disagree with both this premise and this conclusion. Any given version of the standard library (whatever its split into crates like |
@SimonSapin Yes that's true today, but say But perhaps we can have our cake an eat it too. instead of, yes, lying, that std/core come from crates.io today, we can introduce union registries, and say the default one should be |
What's blocking this from becoming a regular RFC PR? |
Thank you everyone involved! Discussion seems stable, so I will be merging this PR, and I will be opening an official RFC for this (after a quick rebase). I'll follow up with a link to the actual RFC PR shortly. |
This is now a full RFC! 🎉 See RFC2663 for further discussion. |
Rendered Here