Skip to content

Commit 2ac382d

Browse files
committed
Auto merge of #10433 - Byron:fix-#10431, r=ehuss
Fix panic when artifact target is used for `[target.'cfg(<target>)'.dependencies` With an artifact dependency like this in package `a`… ```toml [dependencies.a] path = "b" artifact = "bin" target = "$TARGET" ``` …and when using `$TARGET` like this in another package `b`… ```toml [target.'cfg(target_arch = "$ARCHOF_$TARGET")'.dependencies] c = { path = "../c" } ``` …it panics with `thread 'main' panicked at 'activated_features for invalid package: features did not find PackageId <dbg-info>`, but we would expect this to work normally. ### Tasks - [x] reproduce issue in new test - [x] figure out why the test is fixed but the real-world example isn't - [x] find a fix Fixes #10431 and #10452.
2 parents 6b2bf4a + 0b53b1b commit 2ac382d

File tree

4 files changed

+220
-19
lines changed

4 files changed

+220
-19
lines changed

src/cargo/core/compiler/unit_dependencies.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,7 @@ fn compute_deps_custom_build(
464464
// All dependencies of this unit should use profiles for custom builds.
465465
// If this is a build script of a proc macro, make sure it uses host
466466
// features.
467-
let script_unit_for = UnitFor::new_host(
468-
unit_for.is_for_host_features(),
469-
unit_for.root_compile_kind(),
470-
);
467+
let script_unit_for = unit_for.for_custom_build();
471468
// When not overridden, then the dependencies to run a build script are:
472469
//
473470
// 1. Compiling the build script itself.
@@ -782,7 +779,7 @@ fn dep_build_script(
782779
// The profile stored in the Unit is the profile for the thing
783780
// the custom build script is running for.
784781
let profile = state.profiles.get_profile_run_custom_build(&unit.profile);
785-
// UnitFor::new_host is used because we want the `host` flag set
782+
// UnitFor::for_custom_build is used because we want the `host` flag set
786783
// for all of our build dependencies (so they all get
787784
// build-override profiles), including compiling the build.rs
788785
// script itself.
@@ -807,10 +804,7 @@ fn dep_build_script(
807804
// compiled twice. I believe it is not feasible to only build it
808805
// once because it would break a large number of scripts (they
809806
// would think they have the wrong set of features enabled).
810-
let script_unit_for = UnitFor::new_host(
811-
unit_for.is_for_host_features(),
812-
unit_for.root_compile_kind(),
813-
);
807+
let script_unit_for = unit_for.for_custom_build();
814808
new_unit_dep_with_profile(
815809
state,
816810
unit,

src/cargo/core/profiles.rs

+12
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,18 @@ impl UnitFor {
969969
}
970970
}
971971

972+
pub fn for_custom_build(self) -> UnitFor {
973+
UnitFor {
974+
host: true,
975+
host_features: self.host_features,
976+
// Force build scripts to always use `panic=unwind` for now to
977+
// maximally share dependencies with procedural macros.
978+
panic_setting: PanicSetting::AlwaysUnwind,
979+
root_compile_kind: self.root_compile_kind,
980+
artifact_target_for_features: self.artifact_target_for_features,
981+
}
982+
}
983+
972984
/// Set the artifact compile target for use in features using the given `artifact`.
973985
pub(crate) fn with_artifact_features(mut self, artifact: &Artifact) -> UnitFor {
974986
self.artifact_target_for_features = artifact.target().and_then(|t| t.to_compile_target());

src/cargo/core/resolver/features.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -761,18 +761,25 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
761761
) -> Vec<(PackageId, Vec<(&'a Dependency, FeaturesFor)>)> {
762762
// Helper for determining if a platform is activated.
763763
let platform_activated = |dep: &Dependency| -> bool {
764-
// We always care about build-dependencies, and they are always
765-
// Host. If we are computing dependencies "for a build script",
766-
// even normal dependencies are host-only.
767-
if fk == FeaturesFor::HostDep || dep.is_build() {
768-
return self
764+
// We always count platforms as activated if the target stems from an artifact
765+
// dependency's target specification. This triggers in conjunction with
766+
// `[target.'cfg(…)'.dependencies]` manifest sections.
767+
match (dep.is_build(), fk) {
768+
(true, _) | (_, FeaturesFor::HostDep) => {
769+
// We always care about build-dependencies, and they are always
770+
// Host. If we are computing dependencies "for a build script",
771+
// even normal dependencies are host-only.
772+
self.target_data
773+
.dep_platform_activated(dep, CompileKind::Host)
774+
}
775+
(_, FeaturesFor::NormalOrDevOrArtifactTarget(None)) => self
776+
.requested_targets
777+
.iter()
778+
.any(|kind| self.target_data.dep_platform_activated(dep, *kind)),
779+
(_, FeaturesFor::NormalOrDevOrArtifactTarget(Some(target))) => self
769780
.target_data
770-
.dep_platform_activated(dep, CompileKind::Host);
781+
.dep_platform_activated(dep, CompileKind::Target(target)),
771782
}
772-
// Not a build dependency, and not for a build script, so must be Target.
773-
self.requested_targets
774-
.iter()
775-
.any(|kind| self.target_data.dep_platform_activated(dep, *kind))
776783
};
777784
self.resolve
778785
.deps(pkg_id)

tests/testsuite/artifact_dep.rs

+188
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,84 @@ fn features_are_not_unified_among_lib_and_bin_dep_of_different_target() {
425425
.run();
426426
}
427427

428+
#[cargo_test]
429+
fn feature_resolution_works_for_cfg_target_specification() {
430+
if cross_compile::disabled() {
431+
return;
432+
}
433+
let target = cross_compile::alternate();
434+
let p = project()
435+
.file(
436+
"Cargo.toml",
437+
&r#"
438+
[project]
439+
name = "foo"
440+
version = "0.0.1"
441+
authors = []
442+
resolver = "2"
443+
444+
[dependencies.d1]
445+
path = "d1"
446+
artifact = "bin"
447+
target = "$TARGET"
448+
"#
449+
.replace("$TARGET", target),
450+
)
451+
.file(
452+
"src/main.rs",
453+
r#"
454+
fn main() {
455+
let _b = include_bytes!(env!("CARGO_BIN_FILE_D1"));
456+
}
457+
"#,
458+
)
459+
.file(
460+
"d1/Cargo.toml",
461+
&r#"
462+
[package]
463+
name = "d1"
464+
version = "0.0.1"
465+
authors = []
466+
467+
[target.'$TARGET'.dependencies]
468+
d2 = { path = "../d2" }
469+
"#
470+
.replace("$TARGET", target),
471+
)
472+
.file(
473+
"d1/src/main.rs",
474+
r#"fn main() {
475+
d1::f();
476+
}"#,
477+
)
478+
.file("d1/build.rs", r#"fn main() { }"#)
479+
.file(
480+
"d1/src/lib.rs",
481+
&r#"pub fn f() {
482+
#[cfg(target = "$TARGET")]
483+
d2::f();
484+
}
485+
"#
486+
.replace("$TARGET", target),
487+
)
488+
.file(
489+
"d2/Cargo.toml",
490+
r#"
491+
[package]
492+
name = "d2"
493+
version = "0.0.1"
494+
authors = []
495+
"#,
496+
)
497+
.file("d2/build.rs", r#"fn main() { }"#)
498+
.file("d2/src/lib.rs", "pub fn f() {}")
499+
.build();
500+
501+
p.cargo("test -Z bindeps")
502+
.masquerade_as_nightly_cargo()
503+
.run();
504+
}
505+
428506
#[cargo_test]
429507
fn build_script_with_bin_artifacts() {
430508
let p = project()
@@ -2075,3 +2153,113 @@ fn build_script_output_string(p: &Project, package_name: &str) -> String {
20752153
assert_eq!(paths.len(), 1);
20762154
std::fs::read_to_string(&paths[0]).unwrap()
20772155
}
2156+
2157+
#[cargo_test]
2158+
fn build_script_features_for_shared_dependency() {
2159+
// When a build script is built and run, its features should match. Here:
2160+
//
2161+
// foo
2162+
// -> artifact on d1 with target
2163+
// -> common with features f1
2164+
//
2165+
// d1
2166+
// -> common with features f2
2167+
//
2168+
// common has features f1 and f2, with a build script.
2169+
//
2170+
// When common is built as a dependency of d1, it should have features
2171+
// `f2` (for the library and the build script).
2172+
//
2173+
// When common is built as a dependency of foo, it should have features
2174+
// `f1` (for the library and the build script).
2175+
if cross_compile::disabled() {
2176+
return;
2177+
}
2178+
let target = cross_compile::alternate();
2179+
let p = project()
2180+
.file(
2181+
"Cargo.toml",
2182+
&r#"
2183+
[project]
2184+
name = "foo"
2185+
version = "0.0.1"
2186+
resolver = "2"
2187+
2188+
[dependencies]
2189+
d1 = { path = "d1", artifact = "bin", target = "$TARGET" }
2190+
common = { path = "common", features = ["f1"] }
2191+
"#
2192+
.replace("$TARGET", target),
2193+
)
2194+
.file(
2195+
"src/main.rs",
2196+
r#"
2197+
fn main() {
2198+
let _b = include_bytes!(env!("CARGO_BIN_FILE_D1"));
2199+
common::f1();
2200+
}
2201+
"#,
2202+
)
2203+
.file(
2204+
"d1/Cargo.toml",
2205+
r#"
2206+
[package]
2207+
name = "d1"
2208+
version = "0.0.1"
2209+
2210+
[dependencies]
2211+
common = { path = "../common", features = ["f2"] }
2212+
"#,
2213+
)
2214+
.file(
2215+
"d1/src/main.rs",
2216+
r#"fn main() {
2217+
common::f2();
2218+
}"#,
2219+
)
2220+
.file(
2221+
"common/Cargo.toml",
2222+
r#"
2223+
[package]
2224+
name = "common"
2225+
version = "0.0.1"
2226+
2227+
[features]
2228+
f1 = []
2229+
f2 = []
2230+
"#,
2231+
)
2232+
.file(
2233+
"common/src/lib.rs",
2234+
r#"
2235+
#[cfg(feature = "f1")]
2236+
pub fn f1() {}
2237+
2238+
#[cfg(feature = "f2")]
2239+
pub fn f2() {}
2240+
"#,
2241+
)
2242+
.file(
2243+
"common/build.rs",
2244+
&r#"
2245+
use std::env::var_os;
2246+
fn main() {
2247+
assert_eq!(var_os("CARGO_FEATURE_F1").is_some(), cfg!(feature="f1"));
2248+
assert_eq!(var_os("CARGO_FEATURE_F2").is_some(), cfg!(feature="f2"));
2249+
if std::env::var("TARGET").unwrap() == "$TARGET" {
2250+
assert!(var_os("CARGO_FEATURE_F1").is_none());
2251+
assert!(var_os("CARGO_FEATURE_F2").is_some());
2252+
} else {
2253+
assert!(var_os("CARGO_FEATURE_F1").is_some());
2254+
assert!(var_os("CARGO_FEATURE_F2").is_none());
2255+
}
2256+
}
2257+
"#
2258+
.replace("$TARGET", target),
2259+
)
2260+
.build();
2261+
2262+
p.cargo("build -Z bindeps -v")
2263+
.masquerade_as_nightly_cargo()
2264+
.run();
2265+
}

0 commit comments

Comments
 (0)