rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.66k stars 2.4k forks source link

--bin B resolves features differently than -p B in a workspace #8157

Open fabianvdW opened 4 years ago

fabianvdW commented 4 years ago

Problem

Suppose I have a workspace, containing crate A,B and C. A has the feature F, but it is not a default-feature. Both B and C depend on A, however B does not use F, while C does.

The file ./A/src/lib.rs looks like this:

pub fn A(){
    if cfg!(feature = "F"){
        println!("Using library A with Feature F")
    }else {
        println!("Using library A")
    }
}

, while B and C just call A::A() in their main function. Behaviour:

cargo run --bin B
Using library A with Feature F

cargo build
"./target/debug/B.exe"
Using library A with Feature F

cargo run -p B
Using library A

This behaviour just seems inconsistent. I don't understand how B is ever built with F, as in my real use case this imposes a performance penalty for B, so I will have to fall back to using cargo run -p B. The behaviour I expect is:

cargo run --bin B
Using library A

cargo build
"./target/debug/B.exe"
Using library A

cargo run -p B
Using library A

Steps

  1. Make a workspace containing three crates A , B, C.
  2. A/src/lib.rs as above B/src/main.rs:

    fn main() {
    A::A();
    }

    A/Cargo.toml:

    [features]
    default = []
    F = []

    B/Cargo.toml

    [dependencies]
    A = {path = "../A"}

    C/Cargo.toml

    [dependencies.A]
    path = "../A"
    default-features = false
    features = ["F"]
  3. Run cargo commands as above

Possible Solution(s)

Notes

Output of cargo version: cargo 1.43.0 (3532cf738 2020-03-17) Using latest stable Rust, rustc --version: rustc 1.43.0 (4fb7144ed 2020-04-20)

ehuss commented 4 years ago

I can see how that can be confusing. cargo run --bin B has an implicit --workspace flag which means it is building all workspace members, and then filtering out the binary B. Workspace features are unified for all selected packages. Feature resolution looks at which packages are being built, but doesn't know about the filtering operation (which is done after resolution).

It may be very difficult to work around that. Generating the target proposals requires having resolution already being performed.

It may be possible to special-case this scenario and constrain the initial specs (so it would infer if you did --bin B that you also implied -p B), but I can imagine a lot of edge cases that would make that tricky to get right.

fabianvdW commented 4 years ago

But is it actually wanted that even when all packages are built, A isn't build twice, once with F and without F? Atleast in my use-case this is what I would expect from cargo build.

ehuss commented 4 years ago

It's questionable, some projects will want the unification (because it cuts down on compile time), and some do not (because it can alter behavior or prevent compilation). This is tracked in #4463 among others.

fabianvdW commented 4 years ago

I see, in that this case fixing this will just be a minor enhancement, as I can just use the working command. It would be nice if one could just configure such things for each workspace.

Veetaha commented 4 years ago

@ehuss

cargo run --bin B has an implicit --workspace flag which means it is building all workspace

I don't think so, apparently cargo run --bin compiles only what is needed to build the binary (this is very noticable in my project which is a huge cargo workspace) which is what --bin flags' purpose is, isn't it?

ehuss commented 4 years ago

As explained above, it selects the entire workspace, and then filters it down to whichever package contains the named binary. The sequence is roughly:

  1. Select entire workspace (implied --workspace).
  2. Resolve features across entire workspace.
  3. Filter selected packages to find one with the named binary.
  4. Build that binary.

It may be possible to move step 3 above step 2 (so it resolves features only for the selected package), but I think it will be tricky.

UkoeHB commented 10 months ago

This issue is very confusing. I have a WASM binary in the same workspace as a cross-platform binary. The WASM binary was failing to compile because of dep features leaking in from the other binary when using cargo run --bin. I had to basically guess what the problem was, because cargo tree is completely useless for debugging this.

epage commented 4 weeks ago

FYI I've posted rust-lang/rfcs#3692