Skip to content

Commit a846898

Browse files
committed
Fix unit_for computation on proc-macros in shared workspace.
1 parent 94e21ad commit a846898

File tree

3 files changed

+165
-37
lines changed

3 files changed

+165
-37
lines changed

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,7 @@ fn compute_deps(
265265
None => continue,
266266
};
267267
let mode = check_or_build_mode(unit.mode, lib);
268-
let dep_unit_for = unit_for
269-
.with_for_host(lib.for_host())
270-
// If it is a custom build script, then it *only* has build dependencies.
271-
.with_host_features(unit.target.is_custom_build() || lib.proc_macro());
268+
let dep_unit_for = unit_for.with_dependency(unit, lib);
272269

273270
if state.config.cli_unstable().dual_proc_macros && lib.proc_macro() && !unit.kind.is_host()
274271
{
@@ -417,9 +414,7 @@ fn compute_deps_doc(unit: &Unit, state: &mut State<'_, '_>) -> CargoResult<Vec<U
417414
// Rustdoc only needs rmeta files for regular dependencies.
418415
// However, for plugins/proc macros, deps should be built like normal.
419416
let mode = check_or_build_mode(unit.mode, lib);
420-
let dep_unit_for = UnitFor::new_normal()
421-
.with_for_host(lib.for_host())
422-
.with_host_features(lib.proc_macro());
417+
let dep_unit_for = UnitFor::new_normal().with_dependency(unit, lib);
423418
let lib_unit_dep = new_unit_dep(
424419
state,
425420
unit,
@@ -466,12 +461,13 @@ fn maybe_lib(
466461
.find(|t| t.is_linkable())
467462
.map(|t| {
468463
let mode = check_or_build_mode(unit.mode, t);
464+
let dep_unit_for = unit_for.with_dependency(unit, t);
469465
new_unit_dep(
470466
state,
471467
unit,
472468
&unit.pkg,
473469
t,
474-
unit_for,
470+
dep_unit_for,
475471
unit.kind.for_target(t),
476472
mode,
477473
)

src/cargo/core/profiles.rs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use crate::core::compiler::CompileMode;
1+
use crate::core::compiler::{CompileMode, Unit};
22
use crate::core::resolver::features::FeaturesFor;
3-
use crate::core::{Feature, Features, PackageId, PackageIdSpec, Resolve, Shell};
3+
use crate::core::{Feature, Features, PackageId, PackageIdSpec, Resolve, Shell, Target};
44
use crate::util::errors::CargoResultExt;
55
use crate::util::interning::InternedString;
66
use crate::util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
@@ -976,36 +976,38 @@ impl UnitFor {
976976
unit_for
977977
}
978978

979-
/// Returns a new copy based on `for_host` setting.
979+
/// Returns a new copy updated based on the target dependency.
980980
///
981-
/// When `for_host` is true, this clears `panic_abort_ok` in a sticky
982-
/// fashion so that all its dependencies also have `panic_abort_ok=false`.
983-
/// This'll help ensure that once we start compiling for the host platform
984-
/// (build scripts, plugins, proc macros, etc) we'll share the same build
985-
/// graph where everything is `panic=unwind`.
986-
pub fn with_for_host(self, for_host: bool) -> UnitFor {
981+
/// This is where the magic happens that the host/host_features settings
982+
/// transition in a sticky fashion. As the dependency graph is being
983+
/// built, once those flags are set, they stay set for the duration of
984+
/// that portion of tree.
985+
pub fn with_dependency(self, parent: &Unit, dep_target: &Target) -> UnitFor {
986+
// A build script or proc-macro transitions this to being built for the host.
987+
let dep_for_host = dep_target.for_host();
988+
// This is where feature decoupling of host versus target happens.
989+
//
990+
// Once host features are desired, they are always desired.
991+
//
992+
// A proc-macro should always use host features.
993+
//
994+
// Dependencies of a build script should use host features (subtle
995+
// point: the build script itself does *not* use host features, that's
996+
// why the parent is checked here, and not the dependency).
997+
let host_features =
998+
self.host_features || parent.target.is_custom_build() || dep_target.proc_macro();
999+
// Build scripts and proc macros, and all of their dependencies are
1000+
// AlwaysUnwind.
1001+
let panic_setting = if dep_for_host {
1002+
PanicSetting::AlwaysUnwind
1003+
} else {
1004+
self.panic_setting
1005+
};
9871006
UnitFor {
988-
host: self.host || for_host,
989-
host_features: self.host_features,
990-
panic_setting: if for_host {
991-
PanicSetting::AlwaysUnwind
992-
} else {
993-
self.panic_setting
994-
},
995-
}
996-
}
997-
998-
/// Returns a new copy updating it whether or not it should use features
999-
/// for build dependencies and proc-macros.
1000-
///
1001-
/// This is part of the machinery responsible for handling feature
1002-
/// decoupling for build dependencies in the new feature resolver.
1003-
pub fn with_host_features(mut self, host_features: bool) -> UnitFor {
1004-
if host_features {
1005-
assert!(self.host);
1007+
host: self.host || dep_for_host,
1008+
host_features,
1009+
panic_setting,
10061010
}
1007-
self.host_features = self.host_features || host_features;
1008-
self
10091011
}
10101012

10111013
/// Returns `true` if this unit is for a build script or any of its

tests/testsuite/features2.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,3 +2147,133 @@ foo v0.1.0 ([ROOT]/foo)
21472147
.run();
21482148
clear();
21492149
}
2150+
2151+
#[cargo_test]
2152+
fn pm_with_int_shared() {
2153+
// This is a somewhat complex scenario of a proc-macro in a workspace with
2154+
// an integration test where the proc-macro is used for other things, and
2155+
// *everything* is built at once (`--workspace --all-targets
2156+
// --all-features`). There was a bug where the UnitFor settings were being
2157+
// incorrectly computed based on the order that the graph was traversed.
2158+
//
2159+
// There are some uncertainties about exactly how proc-macros should behave
2160+
// with `--workspace`, see https://github.com/rust-lang/cargo/issues/8312.
2161+
//
2162+
// This uses a const-eval hack to do compile-time feature checking.
2163+
let p = project()
2164+
.file(
2165+
"Cargo.toml",
2166+
r#"
2167+
[workspace]
2168+
members = ["foo", "pm", "shared"]
2169+
resolver = "2"
2170+
"#,
2171+
)
2172+
.file(
2173+
"foo/Cargo.toml",
2174+
r#"
2175+
[package]
2176+
name = "foo"
2177+
version = "0.1.0"
2178+
edition = "2018"
2179+
2180+
[dependencies]
2181+
pm = { path = "../pm" }
2182+
shared = { path = "../shared", features = ["norm-feat"] }
2183+
"#,
2184+
)
2185+
.file(
2186+
"foo/src/lib.rs",
2187+
r#"
2188+
// foo->shared always has both features set
2189+
const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==3) as usize];
2190+
"#,
2191+
)
2192+
.file(
2193+
"pm/Cargo.toml",
2194+
r#"
2195+
[package]
2196+
name = "pm"
2197+
version = "0.1.0"
2198+
2199+
[lib]
2200+
proc-macro = true
2201+
2202+
[dependencies]
2203+
shared = { path = "../shared", features = ["host-feat"] }
2204+
"#,
2205+
)
2206+
.file(
2207+
"pm/src/lib.rs",
2208+
r#"
2209+
// pm->shared always has just host
2210+
const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==1) as usize];
2211+
"#,
2212+
)
2213+
.file(
2214+
"pm/tests/pm_test.rs",
2215+
r#"
2216+
// integration test gets both set
2217+
const _CHECK: [(); 0] = [(); 0-!(shared::FEATS==3) as usize];
2218+
"#,
2219+
)
2220+
.file(
2221+
"shared/Cargo.toml",
2222+
r#"
2223+
[package]
2224+
name = "shared"
2225+
version = "0.1.0"
2226+
2227+
[features]
2228+
norm-feat = []
2229+
host-feat = []
2230+
"#,
2231+
)
2232+
.file(
2233+
"shared/src/lib.rs",
2234+
r#"
2235+
pub const FEATS: u32 = {
2236+
if cfg!(feature="norm-feat") && cfg!(feature="host-feat") {
2237+
3
2238+
} else if cfg!(feature="norm-feat") {
2239+
2
2240+
} else if cfg!(feature="host-feat") {
2241+
1
2242+
} else {
2243+
0
2244+
}
2245+
};
2246+
"#,
2247+
)
2248+
.build();
2249+
2250+
p.cargo("build --workspace --all-targets --all-features -v")
2251+
.with_stderr_unordered(
2252+
"\
2253+
[COMPILING] shared [..]
2254+
[RUNNING] `rustc --crate-name shared [..]--crate-type lib [..]
2255+
[RUNNING] `rustc --crate-name shared [..]--crate-type lib [..]
2256+
[RUNNING] `rustc --crate-name shared [..]--test[..]
2257+
[COMPILING] pm [..]
2258+
[RUNNING] `rustc --crate-name pm [..]--crate-type proc-macro[..]
2259+
[RUNNING] `rustc --crate-name pm [..]--test[..]
2260+
[COMPILING] foo [..]
2261+
[RUNNING] `rustc --crate-name foo [..]--test[..]
2262+
[RUNNING] `rustc --crate-name pm_test [..]--test[..]
2263+
[RUNNING] `rustc --crate-name foo [..]--crate-type lib[..]
2264+
[FINISHED] [..]
2265+
",
2266+
)
2267+
.run();
2268+
2269+
// And again, should stay fresh.
2270+
p.cargo("build --workspace --all-targets --all-features -v")
2271+
.with_stderr_unordered(
2272+
"\
2273+
[FRESH] shared [..]
2274+
[FRESH] pm [..]
2275+
[FRESH] foo [..]
2276+
[FINISHED] [..]",
2277+
)
2278+
.run();
2279+
}

0 commit comments

Comments
 (0)