Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upFeatures of dependencies are enabled if they're enabled in build-dependencies; breaks no_std libs #5730
Comments
Pzixel
referenced this issue
Jul 16, 2018
Closed
pwasm-abi-derive is not compatible with no-std libs #54
This comment has been minimized.
This comment has been minimized.
|
Oh wow, it seems like this issue was already reported multiple times: The oldest report is from April 2016, over two years ago. Given that embedded is a target domain of Rust's 2018 roadmap and build scripts are a common stable feature of cargo, I think we should tackle this issue. Especially because it leads to very confusing errors and there are no good workarounds. |
phil-opp
referenced this issue
Jul 16, 2018
Closed
raw-cpuid 4.0.0 depends on heavyweight serde crate #27
This comment has been minimized.
This comment has been minimized.
|
Indeed the intention is to solve this issue! @Eh2406 I'm curious, would you be interested in tackling this issue? I think it has implications on resolution but it's not exclusively concerned with crate graph resolution. There's a lot of interaction with the backend as well where Cargo actually invokes rustc |
This comment has been minimized.
This comment has been minimized.
|
I am happy to help any way I can. However, this has been open for a long time because it is very hard, and tangled with many of the other things that need to be rethought. The goal is that build script and the real build have the same dependency with different features.
... |
This comment has been minimized.
This comment has been minimized.
|
Oh in general I see this as having a pretty simple (sort of) model of what should happen. Right now in cargo's backend we have a graph of "units" where each unit is basically a tuple of (package, target, profile, info). Here we have a precise dependency graph of what to do and each "unit" corresponds to one process, be it rustc, rustdoc, a build script, etc. Occasionally a unit needs to ask "what features are active for me?" but this question doesn't have an answer right now. We can at best coarsely answer "this package has all these features enabled", but that's not correct when you have cross-compiling in effect. The "solution" here is to basically propagate features not through the crate graph resolution, but rather through the unit dependencies. When one unit depends on another it depends on it with a set of features, and those features get unioned over time into a unit, not a package. We can probably make this distinction a bit more coarse and group it by (unit, kind) where kind is in the "info" above and basically says "compile for the target" or "compile for the host". If that all makes sense then the profile bits I don't think interact with features at all. Each unit already has a profile selected as well, so if it did the information is in theory there! I also don't think that public/private dependencies would affect much here too, I think it's mostly just feature propagation? (as we do today, only a little less aggressively). And finally for Cargo.lock features don't come into play either, that's just a resolution of crate graph versions and we pessimistically assume that all features are activated to generate it, so there's no need to record features in Cargo.lock All that leaves build scripts (I think?) and that's indeed true! In theory this doesn't matter as build scripts are compiled with environment variables indicating enabled features, but they're also today compiled with |
This comment has been minimized.
This comment has been minimized.
|
That sounds a lot more doable than I was expecting. Conveniently, Cargo.lock does not record features so that is off the list. Let me try and explain what I was thinking with relation to public/private dependencies. On another concern, do all bild unit dependencies on |
This comment has been minimized.
This comment has been minimized.
|
Hm yeah I'm not sure if there's a connection there either, but it's something I suspect that fleshing out the implementation would show pretty readily! Currently whether or not you're a build or normal dependency all feature calculations are done the same way, you're always compiled with the same set of features in both contexts (which is the bug here) |
This comment has been minimized.
This comment has been minimized.
Agreed, so after this bug is fixed if I depend on
My current question is, I depend on
|
This comment has been minimized.
This comment has been minimized.
|
@Eh2406 for your first scenario, indeed! That's what I imagine for this issue as well. In your second scenario then |
This comment has been minimized.
This comment has been minimized.
Just a random idea, I was thinking that maybe the |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton That would make the change smaller. I am concerned how do we explain the rule for when do "features get unioned". For example:
implies that this only works if cross compiling!? That my main.rs sees @ehuss Lol. Two people respond, two contradictory answers. :-) I am glad to hear that the other alternative is conceivable doable. |
This comment has been minimized.
This comment has been minimized.
|
@Eh2406 another excellent question! This is where you get to a sort of obscure part of Cargo today where let's say you've got a compiler for x86_64-unknown-linux-gnu installed. It turns out these two invocations are actually pretty different:
In the latter invocation Cargo thinks it's cross compiling so it'll build more than the first invocation. This means that if you have a big dependency which is both a target and a build dependency the first command will build it once while the second command will build it twice. The behavior here has been in place since the early days of Cargo but has definitely been brought up as suspect over time. The benefit is that So to answer your question, Cargo already gives an "uncomfortable answer" to when you're not cross compiling you get different results than if you're cross compiling. In that sense I think it's ok to not worry about it with respect to this issue. If we ever fix this issue it'll naturally fall out that features will be separated as well. @ehuss due to the unioning nature of features we have to iteratively walk the dependency graph to discover features enabled, so I think this probably won't be embedded in a |
This comment has been minimized.
This comment has been minimized.
|
So currently edit: that is backwards. build.rs will always see all features. main.rs will only see real deps features in That is a significantly bigger change, but we can chalk that up to just another part of that long standing oddity. |
This comment has been minimized.
This comment has been minimized.
|
@Eh2406 I think you're right yeah! Unfortunately no matter how this is sliced it's a sort of big change :( |
Eh2406
referenced this issue
Jul 21, 2018
Closed
Resolution of dependencies should not be affected by the dependencies of procedural-macro-library. #5760
alexcrichton
referenced this issue
Jul 21, 2018
Open
Provide a way to specify configuration for host compilation #5754
This comment has been minimized.
This comment has been minimized.
|
I'm convinced despite the braking of things this needs to happen. Where should I get started? Relevant: #5656 if we make them more different we should document when to add it to CI. |
This comment has been minimized.
This comment has been minimized.
|
@Eh2406 I think the basic steps here are to delete the |
This comment has been minimized.
This comment has been minimized.
|
Ok so I have a branch with a new |
This comment has been minimized.
This comment has been minimized.
|
@Eh2406 hm from this point I'm not really sure how this would be best done! I haven't thought through this too much myself but I'm fine reviewing any implementation! I suspect the first thing that sticks on the wall will be good enough :) |
This comment has been minimized.
This comment has been minimized.
|
@ehuss I know you have been working on different build args for different target like things. (sorry if I have the terminology wrong.) Do you have advice for me? Specificky is there existing systems/types I should be using/aware of for consistency with your work? |
This comment has been minimized.
This comment has been minimized.
|
IIUC, the intent is to only fix this for the cross-compile case? If that's correct, I don't think it would interact with the profile changes at all. As Alex mentioned, it would probably need to build a map so that given a Unit it can return the correct set of features to use (looking at At least that's my best guess of how I understand it. I'm not terribly familiar with how features or Resolve work, so I don't really know how to compute the feature set. |
This comment has been minimized.
This comment has been minimized.
|
@ehuss That was very helpful! I am not very familiar with how anything but Resolve work, so that gives me somewhere to start. I can definitely do the inner Resolve side, I may bug you again to see if I got the outer part correct. |
This comment has been minimized.
This comment has been minimized.
|
Hm, but thinking about it more, I'm also a little uneasy about only addressing the cross-compile build-dep case. Although that probably covers that vast majority of use cases, it seems like it could be confusing. There have been quite a few feature requests for more granular feature and dependency control. But fixing this one case would be a big win, so it's probably a good first step! |
This comment has been minimized.
This comment has been minimized.
|
So I think for the inner Resolve side I need to do a BFS over the dependency tree to build a sub tree for things needed each Edit: looks like the code you pointed me to is already doing a BFS over the dependency tree, exactly to figure out how all the pieces fit together. So recalculating features should "just" be worked in there, and removed from Resolve as @alexcrichton originally suggested. |
This comment has been minimized.
This comment has been minimized.
|
@ehuss you commented you had free cycles, want to give this a try? |
This comment has been minimized.
This comment has been minimized.
|
I'll take a look. One question I had, when a feature is enabled with slash syntax, should it enable it for both the regular dependency and the build dependency? For example, |
This comment has been minimized.
This comment has been minimized.
|
I just checked whether I had any crates in my cache that optionally enabled features for a build dependency, looks like out of the 145 versions of crates with This script needs
I think eventually we would need to have more control over selecting the type of dependency. Maybe not so much for build dependencies, but for test dependencies I can definitely see situations where testing an optional feature requires activating extra optional features of a dependency just for the tests. It seems like there should be an RFC detailing the full intended design of features and optional dependencies, there's likely to be interaction between fixing this, #5364, #5565, #1596, #1796, #2589 (dup of this), and #3494. Having an overall roadmap of how these would all work together could help answer questions like that. |
This comment has been minimized.
This comment has been minimized.
|
Reading through #5565 some more, I wonder if namespaced features could be extended to cover different dependency contexts as well. |
This comment has been minimized.
This comment has been minimized.
|
+1 for an rfc. +1 for more control eventually. but also a +1 for finding some way to make progress for now and give the no-std people a workaround. |
This comment has been minimized.
This comment has been minimized.
|
I think having It would also fix the example in the OP, as despite that being the uncommon case of having a direct dependency used as both a normal and build dependency it doesn't activate any optional features of it based on its own features. |
This comment has been minimized.
This comment has been minimized.
|
@ehuss FWIW I always figured that the way features would be activated would be by dependency edge. For example when building some artifact there were a number of dependency edges that caused that artifact to be needed to be built, and those edges are what determines features (rather then edges elsewhere "randomly" in the graph). In that sense I figured the slash syntax would basically enable whatever kind of dependency the dependency is listed as. (and if the dependency is listed twice in two sections then "good luck") I'm not opposed to an RFC on this topic but I don't think there's really a whole lot of debate to be had about this. I suspect there's basically "one correct design" and while it probably takes some discussion to reach that point it's not necessarily RFC-scale discussion. |
This comment has been minimized.
This comment has been minimized.
|
I don't see how this can be introduced in a backwards-compatible fashion (without some kind of flag). If you have something like the following: [package]
name = "foo"
version = "0.1.0"
[dependencies]
serde = {version = "1.0", features=["rc"]}
[build-dependencies]
serde = "1.0"Today, the @alexcrichton I'm not sure I understand your comment about the dependency edges. That would mean that features would only be decoupled in the cross-compile scenario, correct? In normal host builds there is usually only one artifact, so build-deps and normal-deps would still be unified. There seems to be a lot of desire to fully decouple features based on the dependency kind (normal/build/dev), and to avoid enabling features for any dependencies that are not used. proc_macro and plugins also complicate things (particularly in the non-cross-compile scenario). It seems like only addressing the cross-compile case would miss some use cases, and might be a confusing behavior. Am I misunderstanding things? |
This comment has been minimized.
This comment has been minimized.
|
Sounds like you understand things well!
This has been a long conversation but here is where I tried to ask the same thing: |
This comment has been minimized.
This comment has been minimized.
|
@ehuss it's true that this is technically a breaking change, but I suspect it's minor enough that we can get away with it. For sure though we'll want to run crater and/or monitor breakage closely to assess the situation. Even if it causes a lot of breakage though I think we'll still want to implement this in one way or another. I also think you're right that this is probably the point where we'll have to bite the bullet and finally make |
added a commit
to bendst/serde
that referenced
this issue
Nov 7, 2018
ehuss
added
the
A-features
label
Nov 18, 2018
This comment has been minimized.
This comment has been minimized.
daxpedda
commented
Nov 30, 2018
|
A comment here: rust-embedded/wg#256 (comment) from @japaric mentions a workaround.
I don't really understand what is meant there or how to do it, can someone be so kind to explain it please? Apparently as mentioned here renaming a crate is also a workaround?: bendst/serde@6be4193 |
This comment has been minimized.
This comment has been minimized.
bendst
commented
Dec 1, 2018
The renamed crate is only used by my custom binary serialization crate. Every build script and proc macro still uses regular serde. It treats serde2 as a completely different dependency. The workaround is a dirty hack and not maintainable :) |
This comment has been minimized.
This comment has been minimized.
daxpedda
commented
Dec 1, 2018
This comment has been minimized.
This comment has been minimized.
I added both workarounds to the issue description. However, they both have significant downsides (not maintainable, std-dependent features not available in build scripts, not possible if it's a build dependency of a dependency) and are not really practical. |
phil-opp commentedJul 16, 2018
•
edited
If a crate is used both as a normal dependency and a build-dependency, features enabled for the build-dependency are also enabled for the normal dependency. This leads to build failures in the
no_stdworld, because anuse_stdfeature that is turned on for the build-dependency is also turned on for the normal dependency.Minimal example
In
bar/foo/Cargo.toml:In
bar/foo/src/lib.rs:In
bar/Cargo.toml:In
bar/src/lib.rs:Compiling with
cargo build --verbose:We see that
--cfg 'feature="default"' --cfg 'feature="use_std"'is passed, i.e. theuse_stdfeature is activated even thoughdefault-features = falseis used. When we comment out the[build-dependencies.foo]section,foois built without theuse_stdfeature.Breaking no_std libs
The problem is that compilation for custom targets using
xargo/cargo-xbuildfails when thestdfeature is activated for the custom target:We see that the
use_stdfeature is turned on even though we are compiling for the custom target. So this is definitely not the build dependency getting built because build scripts are compiled for the host target.Workarounds
There are two ways to work around this, but both have sigificant downsides:
no_stdmode and don't use any std-dependent features and build scripts. This makes writing build scripts harder.Both workarounds don't really work for build dependencies of dependencies.
Versions