From 4677a7cce5a37061d14894516a24c6909470e076 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 13 Dec 2022 20:29:14 +0100 Subject: [PATCH 1/6] test: reproduce transitive bindep dependency bug This is a unit test reproducing the bindep transitive dependency bug based upon examples from - https://github.com/rust-lang/cargo/issues/10837 - https://github.com/rust-lang/cargo/issues/11463 Signed-off-by: Roman Volosatovs --- tests/testsuite/artifact_dep.rs | 223 ++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index 0a39af05b23..cb9ca10b986 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -2447,3 +2447,226 @@ fn with_assumed_host_target_and_optional_build_dep() { ) .run(); } + +#[cargo_test] +fn decouple_same_target_transitive_dep_from_artifact_dep() { + // See https://github.com/rust-lang/cargo/issues/11463 + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = {{ path = "a" }} + bar = {{ path = "bar", artifact = "bin", target = "{target}" }} + "# + ), + ) + .file( + "src/main.rs", + r#" + fn main() {} + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + a = { path = "../a", features = ["feature"] } + "#, + ) + .file( + "bar/src/main.rs", + r#" + fn main() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + + [dependencies] + b = { path = "../b" } + c = { path = "../c" } + + [features] + feature = ["c/feature"] + "#, + ) + .file( + "a/src/lib.rs", + r#" + use b::Trait as _; + + pub fn use_b_trait(x: &impl c::Trait) { + x.b(); + } + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + c = { path = "../c" } + "#, + ) + .file( + "b/src/lib.rs", + r#" + pub trait Trait { + fn b(&self) {} + } + + impl Trait for T {} + "#, + ) + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + + [features] + feature = [] + "#, + ) + .file( + "c/src/lib.rs", + r#" + pub trait Trait {} + "#, + ) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] c v0.1.0 ([CWD]/c) +[COMPILING] b v0.1.0 ([CWD]/b) +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} + +#[cargo_test] +fn decouple_same_target_transitive_dep_from_artifact_dep_lib() { + // See https://github.com/rust-lang/cargo/issues/10837 + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = {{ path = "a" }} + b = {{ path = "b", features = ["feature"] }} + bar = {{ path = "bar", artifact = "bin", lib = true, target = "{target}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + edition = "2021" + + [dependencies] + a = { path = "../a", features = ["b"] } + b = { path = "../b" } + "#, + ) + .file("bar/src/lib.rs", "") + .file( + "bar/src/main.rs", + r#" + use b::Trait; + + fn main() { + a::A.b() + } + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + + [dependencies] + b = { path = "../b", optional = true } + "#, + ) + .file( + "a/src/lib.rs", + r#" + pub struct A; + + #[cfg(feature = "b")] + impl b::Trait for A {} + "#, + ) + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [features] + feature = [] + "#, + ) + .file( + "b/src/lib.rs", + r#" + pub trait Trait { + fn b(&self) {} + } + "#, + ) + .build(); + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] b v0.1.0 ([CWD]/b) +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} From 386645e9901a4f1f293158b5c1627a6d8d4f88ac Mon Sep 17 00:00:00 2001 From: bstrie <865233+bstrie@users.noreply.github.com> Date: Thu, 15 Dec 2022 08:41:04 -0500 Subject: [PATCH 2/6] test: add test for #10525 --- tests/testsuite/artifact_dep.rs | 147 ++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index cb9ca10b986..8e0527137a8 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -2670,3 +2670,150 @@ fn decouple_same_target_transitive_dep_from_artifact_dep_lib() { ) .run(); } + +#[cargo_test] +fn issue_10525() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "mycrate" + version = "0.0.0" + edition = "2021" + + [dependencies] + structopt-derive = {{ path = "structopt-derive" }} + mybindep = {{ path = "mybindep", artifact = "bin", target = "{target}" }} + "# + ), + ) + .file( + "src/main.rs", + r#" + fn main() { + env!("CARGO_BIN_FILE_MYBINDEP"); + } + "#, + ) + .file( + "mybindep/Cargo.toml", + r#" + [package] + name = "mybindep" + version = "0.0.0" + edition = "2021" + + [dependencies] + clap_derive = { path = "../clap_derive" } + "#, + ) + .file("mybindep/src/main.rs", "fn main() {}") + .file( + "clap_derive/Cargo.toml", + r#" + [package] + name = "clap_derive" + version = "0.0.0" + edition = "2021" + + [dependencies] + proc-macro-error = { path = "../proc-macro-error" } + + [lib] + proc-macro = true + "#, + ) + .file("clap_derive/src/lib.rs", "") + .file( + "structopt-derive/Cargo.toml", + r#" + [package] + name = "structopt-derive" + version = "0.0.0" + edition = "2021" + + [dependencies] + syn = { path = "../syn", features = ["parsing"] } + proc-macro-error = { path = "../proc-macro-error" } + + [lib] + proc-macro = true + "#, + ) + .file( + "structopt-derive/src/lib.rs", + r#" + use proc_macro_error::ResultExt; + + fn _parse_structopt_attributes() { + Ok::<(), syn::Error>(()).unwrap_or_abort() + } + "#, + ) + .file( + "proc-macro-error/Cargo.toml", + r#" + [package] + name = "proc-macro-error" + version = "0.0.0" + edition = "2021" + + [dependencies] + syn = { path = "../syn" } + "#, + ) + .file( + "proc-macro-error/src/lib.rs", + r#" + pub trait ResultExt { + fn unwrap_or_abort(self) -> T; + } + + impl> ResultExt for Result { + fn unwrap_or_abort(self) -> T { + panic!() + } + } + + pub struct Diagnostic; + + impl From for Diagnostic { + fn from(_: syn::Error) -> Self { + panic!() + } + } + "#, + ) + .file( + "syn/Cargo.toml", + r#" + [package] + name = "syn" + version = "0.0.0" + edition = "2021" + + [features] + parsing = [] + "#, + ) + .file("syn/src/lib.rs", "pub struct Error;") + .build(); + + p.cargo("build -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr_unordered( + "\ +[COMPILING] mycrate v0.0.0 ([CWD]) +[COMPILING] mybindep v0.0.0 ([CWD]/mybindep) +[COMPILING] clap_derive v0.0.0 ([CWD]/clap_derive) +[COMPILING] structopt-derive v0.0.0 ([CWD]/structopt-derive) +[COMPILING] proc-macro-error v0.0.0 ([CWD]/proc-macro-error) +[COMPILING] syn v0.0.0 ([CWD]/syn) +[FINISHED] dev [..] +", + ) + .run(); +} From 6d43fa64ec9099099ec4c37592868847ebd159f3 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 22 Dec 2022 13:59:24 +0100 Subject: [PATCH 3/6] refactor: simplify rust-lang#10525 test case Signed-off-by: Roman Volosatovs --- tests/testsuite/artifact_dep.rs | 112 +++++++++++++------------------- 1 file changed, 45 insertions(+), 67 deletions(-) diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index 8e0527137a8..dff905d5dd5 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -2672,7 +2672,7 @@ fn decouple_same_target_transitive_dep_from_artifact_dep_lib() { } #[cargo_test] -fn issue_10525() { +fn decouple_same_target_transitive_dep_from_artifact_dep_and_proc_macro() { let target = rustc_host(); let p = project() .file( @@ -2680,139 +2680,117 @@ fn issue_10525() { &format!( r#" [package] - name = "mycrate" - version = "0.0.0" + name = "foo" + version = "0.1.0" edition = "2021" [dependencies] - structopt-derive = {{ path = "structopt-derive" }} - mybindep = {{ path = "mybindep", artifact = "bin", target = "{target}" }} + c = {{ path = "c" }} + bar = {{ path = "bar", artifact = "bin", target = "{target}" }} "# ), ) + .file("src/lib.rs", "") .file( - "src/main.rs", - r#" - fn main() { - env!("CARGO_BIN_FILE_MYBINDEP"); - } - "#, - ) - .file( - "mybindep/Cargo.toml", + "bar/Cargo.toml", r#" [package] - name = "mybindep" - version = "0.0.0" - edition = "2021" + name = "bar" + version = "0.1.0" [dependencies] - clap_derive = { path = "../clap_derive" } + b = { path = "../b" } "#, ) - .file("mybindep/src/main.rs", "fn main() {}") + .file("bar/src/main.rs", "fn main() {}") .file( - "clap_derive/Cargo.toml", + "b/Cargo.toml", r#" [package] - name = "clap_derive" - version = "0.0.0" + name = "b" + version = "0.1.0" edition = "2021" [dependencies] - proc-macro-error = { path = "../proc-macro-error" } + a = { path = "../a" } [lib] proc-macro = true "#, ) - .file("clap_derive/src/lib.rs", "") + .file("b/src/lib.rs", "") .file( - "structopt-derive/Cargo.toml", + "c/Cargo.toml", r#" [package] - name = "structopt-derive" - version = "0.0.0" + name = "c" + version = "0.1.0" edition = "2021" [dependencies] - syn = { path = "../syn", features = ["parsing"] } - proc-macro-error = { path = "../proc-macro-error" } + d = { path = "../d", features = ["feature"] } + a = { path = "../a" } [lib] proc-macro = true "#, ) .file( - "structopt-derive/src/lib.rs", + "c/src/lib.rs", r#" - use proc_macro_error::ResultExt; + use a::Trait; - fn _parse_structopt_attributes() { - Ok::<(), syn::Error>(()).unwrap_or_abort() + fn _c() { + d::D.a() } "#, ) .file( - "proc-macro-error/Cargo.toml", + "a/Cargo.toml", r#" [package] - name = "proc-macro-error" - version = "0.0.0" - edition = "2021" + name = "a" + version = "0.1.0" [dependencies] - syn = { path = "../syn" } + d = { path = "../d" } "#, ) .file( - "proc-macro-error/src/lib.rs", + "a/src/lib.rs", r#" - pub trait ResultExt { - fn unwrap_or_abort(self) -> T; + pub trait Trait { + fn a(&self) {} } - impl> ResultExt for Result { - fn unwrap_or_abort(self) -> T { - panic!() - } - } - - pub struct Diagnostic; - - impl From for Diagnostic { - fn from(_: syn::Error) -> Self { - panic!() - } - } + impl Trait for d::D {} "#, ) .file( - "syn/Cargo.toml", + "d/Cargo.toml", r#" [package] - name = "syn" - version = "0.0.0" - edition = "2021" + name = "d" + version = "0.1.0" [features] - parsing = [] + feature = [] "#, ) - .file("syn/src/lib.rs", "pub struct Error;") + .file("d/src/lib.rs", "pub struct D;") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_unordered( "\ -[COMPILING] mycrate v0.0.0 ([CWD]) -[COMPILING] mybindep v0.0.0 ([CWD]/mybindep) -[COMPILING] clap_derive v0.0.0 ([CWD]/clap_derive) -[COMPILING] structopt-derive v0.0.0 ([CWD]/structopt-derive) -[COMPILING] proc-macro-error v0.0.0 ([CWD]/proc-macro-error) -[COMPILING] syn v0.0.0 ([CWD]/syn) -[FINISHED] dev [..] +[COMPILING] d v0.1.0 ([CWD]/d) +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] b v0.1.0 ([CWD]/b) +[COMPILING] c v0.1.0 ([CWD]/c) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ) .run(); From e99c0bbe28cae76b5a9be4fb3b8ce710d029d686 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 13 Dec 2022 12:17:07 +0100 Subject: [PATCH 4/6] fix: deduplicate dependencies by artifact target Signed-off-by: Roman Volosatovs --- src/cargo/core/compiler/standard_lib.rs | 1 + src/cargo/core/compiler/unit.rs | 18 ++++++++++++++++-- src/cargo/core/compiler/unit_dependencies.rs | 5 +++++ src/cargo/ops/cargo_compile/mod.rs | 4 ++++ src/cargo/ops/cargo_compile/unit_generator.rs | 1 + 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index d1ff7fbd343..2955dce05c5 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -214,6 +214,7 @@ pub fn generate_std_roots( /*is_std*/ true, /*dep_hash*/ 0, IsArtifact::No, + None, )); } } diff --git a/src/cargo/core/compiler/unit.rs b/src/cargo/core/compiler/unit.rs index 32a98cb8cd7..275f5ace62e 100644 --- a/src/cargo/core/compiler/unit.rs +++ b/src/cargo/core/compiler/unit.rs @@ -1,6 +1,8 @@ -use crate::core::compiler::{unit_dependencies::IsArtifact, CompileKind, CompileMode, CrateType}; +use crate::core::compiler::unit_dependencies::IsArtifact; +use crate::core::compiler::{CompileKind, CompileMode, CompileTarget, CrateType}; use crate::core::manifest::{Target, TargetKind}; -use crate::core::{profiles::Profile, Package}; +use crate::core::profiles::Profile; +use crate::core::Package; use crate::util::hex::short_hash; use crate::util::interning::InternedString; use crate::util::Config; @@ -72,6 +74,12 @@ pub struct UnitInner { /// This value initially starts as 0, and then is filled in via a /// second-pass after all the unit dependencies have been computed. pub dep_hash: u64, + + /// This is used for target-dependent feature resolution and is copied from + /// [`FeaturesFor::ArtifactDep`], if the enum matches the variant. + /// + /// [`FeaturesFor::ArtifactDep`]: crate::core::resolver::features::FeaturesFor::ArtifactDep + pub artifact_target_for_features: Option, } impl UnitInner { @@ -139,6 +147,10 @@ impl fmt::Debug for Unit { .field("mode", &self.mode) .field("features", &self.features) .field("artifact", &self.artifact.is_true()) + .field( + "artifact_target_for_features", + &self.artifact_target_for_features, + ) .field("is_std", &self.is_std) .field("dep_hash", &self.dep_hash) .finish() @@ -184,6 +196,7 @@ impl UnitInterner { is_std: bool, dep_hash: u64, artifact: IsArtifact, + artifact_target_for_features: Option, ) -> Unit { let target = match (is_std, target.kind()) { // This is a horrible hack to support build-std. `libstd` declares @@ -216,6 +229,7 @@ impl UnitInterner { is_std, dep_hash, artifact, + artifact_target_for_features, }); Unit { inner } } diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 28a47b0dee8..8d87f4af655 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -885,6 +885,10 @@ fn new_unit_dep_with_profile( .resolve() .is_public_dep(parent.pkg.package_id(), pkg.package_id()); let features_for = unit_for.map_to_features_for(artifact); + let artifact_target = match features_for { + FeaturesFor::ArtifactDep(target) => Some(target), + _ => None, + }; let features = state.activated_features(pkg.package_id(), features_for); let unit = state.interner.intern( pkg, @@ -896,6 +900,7 @@ fn new_unit_dep_with_profile( state.is_std, /*dep_hash*/ 0, artifact.map_or(IsArtifact::No, |_| IsArtifact::Yes), + artifact_target, ); Ok(UnitDep { unit, diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index cae9c993f8d..e251b935763 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -630,6 +630,9 @@ fn traverse_and_share( unit.is_std, new_dep_hash, unit.artifact, + // Since `dep_hash` is now filled in, there's no need to specify the artifact target + // for target-dependent feature resolution + None, ); assert!(memo.insert(unit.clone(), new_unit.clone()).is_none()); new_graph.entry(new_unit.clone()).or_insert(new_deps); @@ -788,6 +791,7 @@ fn override_rustc_crate_types( unit.is_std, unit.dep_hash, unit.artifact, + unit.artifact_target_for_features, ) }; units[0] = match unit.target.kind() { diff --git a/src/cargo/ops/cargo_compile/unit_generator.rs b/src/cargo/ops/cargo_compile/unit_generator.rs index 53012dcd03c..bc1528e5867 100644 --- a/src/cargo/ops/cargo_compile/unit_generator.rs +++ b/src/cargo/ops/cargo_compile/unit_generator.rs @@ -171,6 +171,7 @@ impl<'a> UnitGenerator<'a, '_> { /*is_std*/ false, /*dep_hash*/ 0, IsArtifact::No, + None, ) }) .collect() From 548b2528fb1a1c725a5aef14c80d9a401deb3ae9 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 22 Dec 2022 16:16:02 +0100 Subject: [PATCH 5/6] test: reproduce bindep dependency collision bug Signed-off-by: Roman Volosatovs --- tests/testsuite/artifact_dep.rs | 59 +++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index dff905d5dd5..e8943beaa40 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -2795,3 +2795,62 @@ fn decouple_same_target_transitive_dep_from_artifact_dep_and_proc_macro() { ) .run(); } + +#[cargo_test] +fn same_target_artifact_dep_sharing() { + let target = rustc_host(); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + a = {{ path = "a" }} + bar = {{ path = "bar", artifact = "bin", target = "{target}" }} + "# + ), + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [dependencies] + a = { path = "../a" } + "#, + ) + .file( + "bar/src/main.rs", + r#" + fn main() {} + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + p.cargo(&format!("build -Z bindeps --target {target}")) + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] a v0.1.0 ([CWD]/a) +[COMPILING] bar v0.1.0 ([CWD]/bar) +[COMPILING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} From 385bba3cf29be0e1bdd1e04066edc64650ec5321 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Thu, 22 Dec 2022 14:44:05 +0100 Subject: [PATCH 6/6] fix: share artifact and build dependencies This prevents collisions for transitive dependencies Signed-off-by: Roman Volosatovs --- src/cargo/ops/cargo_compile/mod.rs | 42 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index e251b935763..3022390dd6e 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -415,20 +415,25 @@ pub fn create_bcx<'a, 'cfg>( remove_duplicate_doc(build_config, &units, &mut unit_graph); } - if build_config + let host_kind_requested = build_config .requested_kinds .iter() - .any(CompileKind::is_host) - { + .any(CompileKind::is_host); + let should_share_deps = host_kind_requested + || config.cli_unstable().bindeps + && unit_graph + .iter() + .any(|(unit, _)| unit.artifact_target_for_features.is_some()); + if should_share_deps { // Rebuild the unit graph, replacing the explicit host targets with - // CompileKind::Host, merging any dependencies shared with build - // dependencies. + // CompileKind::Host, removing `artifact_target_for_features` and merging any dependencies + // shared with build and artifact dependencies. let new_graph = rebuild_unit_graph_shared( interner, unit_graph, &units, &scrape_units, - explicit_host_kind, + host_kind_requested.then_some(explicit_host_kind), ); // This would be nicer with destructuring assignment. units = new_graph.0; @@ -540,29 +545,31 @@ pub fn create_bcx<'a, 'cfg>( /// This is used to rebuild the unit graph, sharing host dependencies if possible. /// /// This will translate any unit's `CompileKind::Target(host)` to -/// `CompileKind::Host` if the kind is equal to `to_host`. This also handles -/// generating the unit `dep_hash`, and merging shared units if possible. +/// `CompileKind::Host` if `to_host` is not `None` and the kind is equal to `to_host`. +/// This also handles generating the unit `dep_hash`, and merging shared units if possible. /// /// This is necessary because if normal dependencies used `CompileKind::Host`, /// there would be no way to distinguish those units from build-dependency -/// units. This can cause a problem if a shared normal/build dependency needs +/// units or artifact dependency units. +/// This can cause a problem if a shared normal/build/artifact dependency needs /// to link to another dependency whose features differ based on whether or -/// not it is a normal or build dependency. If both units used +/// not it is a normal, build or artifact dependency. If all units used /// `CompileKind::Host`, then they would end up being identical, causing a /// collision in the `UnitGraph`, and Cargo would end up randomly choosing one /// value or the other. /// -/// The solution is to keep normal and build dependencies separate when +/// The solution is to keep normal, build and artifact dependencies separate when /// building the unit graph, and then run this second pass which will try to /// combine shared dependencies safely. By adding a hash of the dependencies /// to the `Unit`, this allows the `CompileKind` to be changed back to `Host` -/// without fear of an unwanted collision. +/// and `artifact_target_for_features` to be removed without fear of an unwanted +/// collision for build or artifact dependencies. fn rebuild_unit_graph_shared( interner: &UnitInterner, unit_graph: UnitGraph, roots: &[Unit], scrape_units: &[Unit], - to_host: CompileKind, + to_host: Option, ) -> (Vec, Vec, UnitGraph) { let mut result = UnitGraph::new(); // Map of the old unit to the new unit, used to avoid recursing into units @@ -595,7 +602,7 @@ fn traverse_and_share( new_graph: &mut UnitGraph, unit_graph: &UnitGraph, unit: &Unit, - to_host: CompileKind, + to_host: Option, ) -> Unit { if let Some(new_unit) = memo.get(unit) { // Already computed, no need to recompute. @@ -615,10 +622,9 @@ fn traverse_and_share( }) .collect(); let new_dep_hash = dep_hash.finish(); - let new_kind = if unit.kind == to_host { - CompileKind::Host - } else { - unit.kind + let new_kind = match to_host { + Some(to_host) if to_host == unit.kind => CompileKind::Host, + _ => unit.kind, }; let new_unit = interner.intern( &unit.pkg,