Closed cbeck88 closed 4 years ago
Duplicate of #4361
and if it's not exactly #4361, then it's probably one of:
@dwijnand I don't see that any of those tickets has to do with my issue -- they are all about features, and there are no cfg features in my example, I am using cfg
of target-arch
This issue specifically has to do with this syntax:
[target.'cfg(not(target_arch = "x86_64"))'.dependencies]
rand = { version = "0.6" }
Cargo is giving me std
on x86_64
arch because these two lines are present, which doesn't make any sense, and when we comment them out, it stops doing that.
This has nothing to do with default features, or features being unioned across the build plan. rand
is not a dependency of my crate on x86_64
, nevertheless std
gets linked in when I build. That's the bug.
Let me premise by saying we can reopen if I'm wrong, but I think that's already covered by these comments in those issues:
So, I'm skeptical that this is really the same issue, because when we change it from using target_arch
in the cfg
expressions to simply adding a feature, and making rand
an optional dependency gated on that feature, it simply works, in our actual project in tree.
I can try to add such a demonstration in the minimal verifiable example.
This suggests that there is more to the story than e.g. @alexcrichton 's response to this comment: https://github.com/rust-lang/cargo/issues/2589#issuecomment-224697180
When we build the resolution graph today it contains information about all targets, and the filtering per platform only happens at the very end when we're compiling.
otherwise why doesn't the same issue occur when we branch on features instead of target_arch
?
Contrary to what is written there in 2016, It seems that cargo is successfully able to avoid building rand
in x86_64, AND when using the cfg(not(target_arch = ...))
expression. The discrepancy in my example is specific to std
and not any other crate.
A minimal verificable example would be lovely, thank you.
A little bit of digging into it shows the problem seems to be cargo decided to pass --cfg 'feature="std"' when compiling rand_core, see the following different calls of rustc(you can generate then with cargo build -vv)
LD_LIBRARY_PATH='/C/Development/rust-cargo-bug/target/debug/deps:/home/lijinpei/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:/home/lijinpei/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib' CARGO_PKG_NAME=rand_core CARGO_PKG_DESCRIPTION='Core random number generator traits and tools for implementation. ' CARGO_PKG_VERSION_MINOR=4 CARGO_PKG_HOMEPAGE='https://crates.io/crates/rand_core' CARGO=/home/lijinpei/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_PKG_AUTHORS='The Rand Project Developers:The Rust Project Developers' CARGO_MANIFEST_DIR=/home/lijinpei/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.4.0 CARGO_PKG_VERSION_MAJOR=0 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE= CARGO_PKG_VERSION=0.4.0 CARGO_PKG_REPOSITORY='https://github.com/rust-random/rand' rustc --crate-name rand_core /home/lijinpei/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.4.0/src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=afbd3cacdc17ebd3 -C extra-filename=-afbd3cacdc17ebd3 --out-dir /C/Development/rust-cargo-bug/target/debug/deps -L dependency=/C/Development/rust-cargo-bug/target/debug/deps --cap-lints warn
LD_LIBRARY_PATH='/C/Development/rust-cargo-bug/target/debug/deps:/home/lijinpei/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:/home/lijinpei/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib' CARGO_PKG_DESCRIPTION='Core random number generator traits and tools for implementation. ' CARGO_PKG_AUTHORS='The Rand Project Developers:The Rust Project Developers' CARGO_PKG_NAME=rand_core CARGO_PKG_VERSION_MINOR=4 CARGO_MANIFEST_DIR=/home/lijinpei/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.4.0 CARGO_PKG_VERSION=0.4.0 CARGO_PKG_REPOSITORY='https://github.com/rust-random/rand' CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_HOMEPAGE='https://crates.io/crates/rand_core' CARGO_PKG_VERSION_PRE= CARGO=/home/lijinpei/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_PKG_VERSION_MAJOR=0 rustc --crate-name rand_core /home/lijinpei/.cargo/registry/src/github.com-1ecc6299db9ec823/rand_core-0.4.0/src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 --cfg 'feature="alloc"' --cfg 'feature="std"' -C metadata=fac15169af3958da -C extra-filename=-fac15169af3958da --out-dir /C/Development/rust-cargo-bug/target/debug/deps -L dependency=/C/Development/rust-cargo-bug/target/debug/deps --cap-lints warn
(the first one is the correct one).
I think the problem is, when cargo computes Resolve(a data-structure contains which package are dependent by which, and which version and features), it treat take targets-specific dependencies as they are always needed. So rand's mere appearance (even through not later linked in) cause it's std feature, and therefore rand_core's std feature to be included, which cause problem when rand_core is linked with myenslave.
For example, when the crate doesn't compiles, Resolve contains the followings:
pkg "rand_core" version 0.3.1 features:
pkg "rand_isaac" version 0.1.1 features:
pkg "cloudabi" version 0.0.3 features: bitflags default
pkg "rand_os" version 0.1.2 features:
pkg "rand_chacha" version 0.1.1 features:
pkg "winapi-x86_64-pc-windows-gnu" version 0.4.0 features:
pkg "myenclave" version 1.0.0 features:
pkg "semver" version 0.9.0 features: default
pkg "autocfg" version 0.1.2 features:
pkg "rdrand" version 0.4.0 features: std default
pkg "winapi" version 0.3.6 features: ntsecapi minwindef profileapi winnt
pkg "winapi-i686-pc-windows-gnu" version 0.4.0 features:
pkg "semver-parser" version 0.7.0 features:
pkg "fuchsia-cprng" version 0.1.1 features:
pkg "rand_jitter" version 0.1.3 features: std rand_core
pkg "bitflags" version 1.0.4 features: default
pkg "rand" version 0.6.5 features: std alloc default rand_jitter rand_core rand_os
pkg "mcrand" version 1.0.0 features:
pkg "libc" version 0.2.48 features: use_std default
pkg "rand_pcg" version 0.1.1 features:
pkg "rand_hc" version 0.1.0 features:
pkg "rustc_version" version 0.2.3 features:
pkg "rand_core" version 0.4.0 features: alloc std
pkg "rand_xorshift" version 0.1.1 features:
If we write the following in Cargo.toml:
[target.'cfg(not(target_arch = "x86_64"))'.dependencies] rand = { version = "0.6", default-features = false }
then Resolve would be:
pkg "winapi" version 0.3.6 features: profileapi minwindef winnt ntsecapi
pkg "rand_xorshift" version 0.1.1 features:
pkg "rand_pcg" version 0.1.1 features:
pkg "rand_isaac" version 0.1.1 features:
pkg "winapi-i686-pc-windows-gnu" version 0.4.0 features:
pkg "rand_hc" version 0.1.0 features:
pkg "libc" version 0.2.48 features:
pkg "rand_core" version 0.4.0 features:
pkg "rand_jitter" version 0.1.3 features:
pkg "mcrand" version 1.0.0 features:
pkg "rand_chacha" version 0.1.1 features:
pkg "semver-parser" version 0.7.0 features:
pkg "rustc_version" version 0.2.3 features:
pkg "rand_core" version 0.3.1 features:
pkg "winapi-x86_64-pc-windows-gnu" version 0.4.0 features:
pkg "rand" version 0.6.5 features:
pkg "semver" version 0.9.0 features: default
pkg "autocfg" version 0.1.2 features:
pkg "myenclave" version 1.0.0 features:
and the project compiles.
@lijinpei thank you for digging into this!
So my takeaway from this is that making something optional
is somehow not as strong as making it `cfg(target_arch(...))`` selected -- that's confusing to me but if you think this behavior is reliable it's really helpful!
Do you think that this a "bug" or a design decision of the cargo team? (Or simply not clear which?)
First, theoretically, whether core_rand's std feature enabled or not, it meets all the dependency requirements. A requirements problem may be satisfied by multiple version of a set of packages, or multiple sets of feature of packages, I don't know if cargo guarantees to return a specific set.
Secondly, currently, when computing dependencies, cargo treat all platform-specific dependencies as if they are always needed, it's only when the actual build starts the platform-specifics are filtered out. I personally thought this is a bug. But that needs to be cleared be cargo team. (I can see one reason for cargo to work this way is it needs to support cross-build while share the same Cargo.lock. But maybe constraints for different platforms may not be satisfied together?)
(A third problem would be features like std needs consensus between rustc and cargo, but cargo won't look into a .rs file's contents, it even doesn't know which set of files are needed to build a crate without rustc's help, while rustc doesn't know package dependencies, it needs cargo to provides them on the command line)
Might be related to https://github.com/rust-lang/cargo/issues/2524?
@lijinpei in any other build system like, scons, cmake, make, if unnecessary stuff is being built, that is a bug. All of these other systems are highly configurable so that you can make it build exactly what you need with exactly the flags you need.
It's crazy that in cargo you can have bugs where the wrong stuff is built, it takes hours to track down the reason, and then there's no fix, or maybe a hack requiring changes to everything, and the cargo devs question whether it's a bug! Building unnecessary stuff can mean slow builds or broken builds. Bugs like this in cargo (and there are many of them!), and the difficulty of debugging them, are a serious reason not to use rust at all, esp. in embedded development. In our company we have worked around it with a hack but it's pretty dirty.
You can choose not to define it as a bug, but this just means you have set the bar for cargo way below other similar tools.
I can see one reason for cargo to work this way is it needs to support cross-build while share the same Cargo.lock
I don't see a reason for this constraint, any more than it would make sense to require Cargo.lock to be the same no matter what features are enabled.
I'm going to close this as I believe it is resolved by #7914.
Problem
In the minimal test case (https://github.com/cbeck88/rust-cargo-bug), a library is marked
no_std
and which has no dependency onstd
is nevertheless linked tostd
by cargo, wrongly. This breaks the build, because it conflicts with provided lang items.Yet, when a conditional dependency which is not even selected or built by cargo is commented out in
Cargo.toml
,cargo
stops linkingstd
to the first target, and the build succeeds.This indicates that the logic that determines whether a crate transitively depends on
std
is broken and doesn't take into account conditional dependencies properly.Steps
To reproduce the bug, use the minimal verifiable example provided here: https://github.com/cbeck88/rust-cargo-bug
clone
the repositorycargo build
, orcargo build --target=x86_64-unknown-linux-gnu
if you are not on an x86_64 machine. Observe build failuregit apply
the patch, which comments out conditional dependency onrand
Possible Solution(s)
I suspect that the logic that determines whether
std
is needed, ignores, or doesn't properly take into account, whether or not a conditional dependency was actually selected.Notes
Output of
cargo version
:Using stable cargo and rust on Ubuntu 18.04: