Skip to content

Commit 508aad9

Browse files
committed
Add member table to cargo config
The member table right now only contains the `target` member. It defines the build target for a single workspace member overriding both the `build.target` config value and the '--target' switch. Fixes rust-lang#7004
1 parent af64bd6 commit 508aad9

File tree

7 files changed

+212
-95
lines changed

7 files changed

+212
-95
lines changed

src/bin/cargo/cli.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Available unstable (nightly-only) flags:
4242
-Z timings -- Display concurrency information
4343
-Z doctest-xcompile -- Compile and run doctests for non-host target using runner config
4444
-Z terminal-width -- Provide a terminal width to rustc for error truncation
45+
-Z member-configs -- Configure individual workspace members in cargo config
4546
4647
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
4748
);

src/cargo/core/compiler/context/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,14 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
287287
let dest = self.bcx.profiles.get_dir_name();
288288
let host_layout = Layout::new(self.bcx.ws, None, &dest)?;
289289
let mut targets = HashMap::new();
290+
let mut kinds = HashSet::new();
290291
for kind in self.bcx.build_config.requested_kinds.iter() {
292+
kinds.insert(*kind);
293+
}
294+
for root in self.bcx.roots.iter() {
295+
kinds.insert(root.kind());
296+
}
297+
for kind in kinds.iter() {
291298
if let CompileKind::Target(target) = *kind {
292299
let layout = Layout::new(self.bcx.ws, Some(target), &dest)?;
293300
targets.insert(target, layout);

src/cargo/core/compiler/unit.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ impl Unit {
100100
pub fn buildkey(&self) -> String {
101101
format!("{}-{}", self.pkg.name(), short_hash(self))
102102
}
103+
104+
pub fn kind(&self) -> CompileKind {
105+
self.inner.kind
106+
}
103107
}
104108

105109
// Just hash the pointer for fast hashing

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ pub struct CliUnstable {
360360
pub multitarget: bool,
361361
pub rustdoc_map: bool,
362362
pub terminal_width: Option<Option<usize>>,
363+
pub member_configs: bool,
363364
}
364365

365366
fn deserialize_build_std<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
@@ -465,6 +466,7 @@ impl CliUnstable {
465466
"multitarget" => self.multitarget = parse_empty(k, v)?,
466467
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
467468
"terminal-width" => self.terminal_width = Some(parse_usize_opt(v)?),
469+
"member-configs" => self.member_configs = parse_empty(k, v)?,
468470
_ => bail!("unknown `-Z` flag specified: {}", k),
469471
}
470472

src/cargo/ops/cargo_compile.rs

Lines changed: 141 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,20 @@ pub fn create_bcx<'a, 'cfg>(
314314
}
315315
config.validate_term_config()?;
316316

317-
let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
317+
let mut target_data_kinds = HashSet::new();
318+
for requested_kind in build_config.requested_kinds.iter() {
319+
target_data_kinds.insert(*requested_kind);
320+
}
321+
if config.cli_unstable().member_configs {
322+
for (_, member_config) in config.member_cfgs()?.iter() {
323+
if let Some(target) = &member_config.target {
324+
let kind = CompileKind::Target(CompileTarget::new(target.raw_value())?);
325+
target_data_kinds.insert(kind);
326+
}
327+
}
328+
}
329+
let target_data_kinds: Vec<_> = target_data_kinds.into_iter().collect();
330+
let target_data = RustcTargetData::new(ws, &target_data_kinds)?;
318331

319332
let specs = spec.to_package_id_specs(ws)?;
320333
let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode);
@@ -414,24 +427,13 @@ pub fn create_bcx<'a, 'cfg>(
414427
workspace_resolve.as_ref().unwrap_or(&resolve),
415428
)?;
416429

417-
// If `--target` has not been specified, then the unit graph is built
418-
// assuming `--target $HOST` was specified. See
419-
// `rebuild_unit_graph_shared` for more on why this is done.
420430
let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
421-
let explicit_host_kinds: Vec<_> = build_config
422-
.requested_kinds
423-
.iter()
424-
.map(|kind| match kind {
425-
CompileKind::Host => explicit_host_kind,
426-
CompileKind::Target(t) => CompileKind::Target(*t),
427-
})
428-
.collect();
429-
430431
let mut units = generate_targets(
431432
ws,
432433
&to_builds,
433434
filter,
434-
&explicit_host_kinds,
435+
&build_config.requested_kinds,
436+
explicit_host_kind,
435437
build_config.mode,
436438
&resolve,
437439
&workspace_resolve,
@@ -441,6 +443,12 @@ pub fn create_bcx<'a, 'cfg>(
441443
interner,
442444
)?;
443445

446+
let mut explicit_kinds = HashSet::new();
447+
for unit in units.iter() {
448+
explicit_kinds.insert(unit.kind());
449+
}
450+
let explicit_kinds: Vec<_> = explicit_kinds.into_iter().collect();
451+
444452
let std_roots = if let Some(crates) = &config.cli_unstable().build_std {
445453
// Only build libtest if it looks like it is needed.
446454
let mut crates = crates.clone();
@@ -459,7 +467,7 @@ pub fn create_bcx<'a, 'cfg>(
459467
&crates,
460468
std_resolve,
461469
std_features,
462-
&explicit_host_kinds,
470+
&explicit_kinds,
463471
&pkg_set,
464472
interner,
465473
&profiles,
@@ -731,6 +739,7 @@ fn generate_targets(
731739
packages: &[&Package],
732740
filter: &CompileFilter,
733741
requested_kinds: &[CompileKind],
742+
explicit_host_kind: CompileKind,
734743
mode: CompileMode,
735744
resolve: &Resolve,
736745
workspace_resolve: &Option<Resolve>,
@@ -741,91 +750,112 @@ fn generate_targets(
741750
) -> CargoResult<Vec<Unit>> {
742751
let config = ws.config();
743752
// Helper for creating a list of `Unit` structures
744-
let new_unit =
745-
|units: &mut HashSet<Unit>, pkg: &Package, target: &Target, target_mode: CompileMode| {
746-
let unit_for = if target_mode.is_any_test() {
747-
// NOTE: the `UnitFor` here is subtle. If you have a profile
748-
// with `panic` set, the `panic` flag is cleared for
749-
// tests/benchmarks and their dependencies. If this
750-
// was `normal`, then the lib would get compiled three
751-
// times (once with panic, once without, and once with
752-
// `--test`).
753-
//
754-
// This would cause a problem for doc tests, which would fail
755-
// because `rustdoc` would attempt to link with both libraries
756-
// at the same time. Also, it's probably not important (or
757-
// even desirable?) for rustdoc to link with a lib with
758-
// `panic` set.
759-
//
760-
// As a consequence, Examples and Binaries get compiled
761-
// without `panic` set. This probably isn't a bad deal.
762-
//
763-
// Forcing the lib to be compiled three times during `cargo
764-
// test` is probably also not desirable.
765-
UnitFor::new_test(config)
766-
} else if target.for_host() {
767-
// Proc macro / plugin should not have `panic` set.
768-
UnitFor::new_compiler()
769-
} else {
770-
UnitFor::new_normal()
771-
};
772-
// Custom build units are added in `build_unit_dependencies`.
773-
assert!(!target.is_custom_build());
774-
let target_mode = match target_mode {
775-
CompileMode::Test => {
776-
if target.is_example() && !filter.is_specific() && !target.tested() {
777-
// Examples are included as regular binaries to verify
778-
// that they compile.
779-
CompileMode::Build
780-
} else {
781-
CompileMode::Test
782-
}
753+
let new_unit = |units: &mut HashSet<Unit>,
754+
pkg: &Package,
755+
target: &Target,
756+
target_mode: CompileMode,
757+
force_kind: Option<CompileKind>| {
758+
let unit_for = if target_mode.is_any_test() {
759+
// NOTE: the `UnitFor` here is subtle. If you have a profile
760+
// with `panic` set, the `panic` flag is cleared for
761+
// tests/benchmarks and their dependencies. If this
762+
// was `normal`, then the lib would get compiled three
763+
// times (once with panic, once without, and once with
764+
// `--test`).
765+
//
766+
// This would cause a problem for doc tests, which would fail
767+
// because `rustdoc` would attempt to link with both libraries
768+
// at the same time. Also, it's probably not important (or
769+
// even desirable?) for rustdoc to link with a lib with
770+
// `panic` set.
771+
//
772+
// As a consequence, Examples and Binaries get compiled
773+
// without `panic` set. This probably isn't a bad deal.
774+
//
775+
// Forcing the lib to be compiled three times during `cargo
776+
// test` is probably also not desirable.
777+
UnitFor::new_test(config)
778+
} else if target.for_host() {
779+
// Proc macro / plugin should not have `panic` set.
780+
UnitFor::new_compiler()
781+
} else {
782+
UnitFor::new_normal()
783+
};
784+
// Custom build units are added in `build_unit_dependencies`.
785+
assert!(!target.is_custom_build());
786+
let target_mode = match target_mode {
787+
CompileMode::Test => {
788+
if target.is_example() && !filter.is_specific() && !target.tested() {
789+
// Examples are included as regular binaries to verify
790+
// that they compile.
791+
CompileMode::Build
792+
} else {
793+
CompileMode::Test
783794
}
784-
CompileMode::Build => match *target.kind() {
785-
TargetKind::Test => CompileMode::Test,
786-
TargetKind::Bench => CompileMode::Bench,
787-
_ => CompileMode::Build,
788-
},
789-
// `CompileMode::Bench` is only used to inform `filter_default_targets`
790-
// which command is being used (`cargo bench`). Afterwards, tests
791-
// and benches are treated identically. Switching the mode allows
792-
// de-duplication of units that are essentially identical. For
793-
// example, `cargo build --all-targets --release` creates the units
794-
// (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
795-
// and since these are the same, we want them to be de-duplicated in
796-
// `unit_dependencies`.
797-
CompileMode::Bench => CompileMode::Test,
798-
_ => target_mode,
799-
};
795+
}
796+
CompileMode::Build => match *target.kind() {
797+
TargetKind::Test => CompileMode::Test,
798+
TargetKind::Bench => CompileMode::Bench,
799+
_ => CompileMode::Build,
800+
},
801+
// `CompileMode::Bench` is only used to inform `filter_default_targets`
802+
// which command is being used (`cargo bench`). Afterwards, tests
803+
// and benches are treated identically. Switching the mode allows
804+
// de-duplication of units that are essentially identical. For
805+
// example, `cargo build --all-targets --release` creates the units
806+
// (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
807+
// and since these are the same, we want them to be de-duplicated in
808+
// `unit_dependencies`.
809+
CompileMode::Bench => CompileMode::Test,
810+
_ => target_mode,
811+
};
800812

801-
let is_local = pkg.package_id().source_id().is_path();
802-
let profile = profiles.get_profile(
803-
pkg.package_id(),
804-
ws.is_member(pkg),
805-
is_local,
806-
unit_for,
807-
target_mode,
808-
);
813+
let is_local = pkg.package_id().source_id().is_path();
814+
let profile = profiles.get_profile(
815+
pkg.package_id(),
816+
ws.is_member(pkg),
817+
is_local,
818+
unit_for,
819+
target_mode,
820+
);
809821

810-
// No need to worry about build-dependencies, roots are never build dependencies.
811-
let features_for = FeaturesFor::from_for_host(target.proc_macro());
812-
let features = resolved_features.activated_features(pkg.package_id(), features_for);
822+
// No need to worry about build-dependencies, roots are never build dependencies.
823+
let features_for = FeaturesFor::from_for_host(target.proc_macro());
824+
let features = resolved_features.activated_features(pkg.package_id(), features_for);
813825

814-
for kind in requested_kinds {
815-
let unit = interner.intern(
816-
pkg,
817-
target,
818-
profile,
819-
kind.for_target(target),
820-
target_mode,
821-
features.clone(),
822-
/*is_std*/ false,
823-
/*dep_hash*/ 0,
824-
);
825-
units.insert(unit);
826-
}
826+
// If `force_kind` is given we build only that kind
827+
let requested_kinds = if let Some(force_kind) = force_kind {
828+
vec![force_kind]
829+
} else {
830+
requested_kinds.to_vec()
827831
};
828832

833+
// If `--target` has not been specified, then the unit graph is built
834+
// assuming `--target $HOST` was specified. See
835+
// `rebuild_unit_graph_shared` for more on why this is done.
836+
let explicit_kinds: Vec<_> = requested_kinds
837+
.iter()
838+
.map(|kind| match kind {
839+
CompileKind::Host => explicit_host_kind,
840+
CompileKind::Target(t) => CompileKind::Target(*t),
841+
})
842+
.collect();
843+
844+
for kind in explicit_kinds {
845+
let unit = interner.intern(
846+
pkg,
847+
target,
848+
profile,
849+
kind.for_target(target),
850+
target_mode,
851+
features.clone(),
852+
/*is_std*/ false,
853+
/*dep_hash*/ 0,
854+
);
855+
units.insert(unit);
856+
}
857+
};
858+
829859
// Create a list of proposed targets.
830860
let mut proposals: Vec<Proposal<'_>> = Vec::new();
831861

@@ -978,7 +1008,23 @@ fn generate_targets(
9781008
None => Vec::new(),
9791009
};
9801010
if target.is_lib() || unavailable_features.is_empty() {
981-
new_unit(&mut units, pkg, target, mode);
1011+
let force_kind =
1012+
if let Some(member_config) = config.member_config(pkg.name().as_str())? {
1013+
if !config.cli_unstable().member_configs {
1014+
anyhow::bail!(
1015+
"member configurations are unstable. Add -Zmember-configs to enable them."
1016+
);
1017+
}
1018+
if let Some(ref target) = member_config.target {
1019+
let target = target.raw_value().to_string();
1020+
Some(CompileKind::from_requested_targets(config, &[target])?[0])
1021+
} else {
1022+
None
1023+
}
1024+
} else {
1025+
None
1026+
};
1027+
new_unit(&mut units, pkg, target, mode, force_kind);
9821028
} else if requires_features {
9831029
let required_features = target.required_features().unwrap();
9841030
let quoted_required_features: Vec<String> = required_features

src/cargo/util/config/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ pub struct Config {
177177
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
178178
doc_extern_map: LazyCell<RustdocExternMap>,
179179
progress_config: ProgressConfig,
180+
member_cfgs: LazyCell<HashMap<String, CargoMemberConfig>>,
180181
}
181182

182183
impl Config {
@@ -249,6 +250,7 @@ impl Config {
249250
target_cfgs: LazyCell::new(),
250251
doc_extern_map: LazyCell::new(),
251252
progress_config: ProgressConfig::default(),
253+
member_cfgs: LazyCell::new(),
252254
}
253255
}
254256

@@ -1226,6 +1228,17 @@ impl Config {
12261228
target::load_target_triple(self, target)
12271229
}
12281230

1231+
/// Returns a map of [member.'crate'] tables.
1232+
pub fn member_cfgs(&self) -> CargoResult<&HashMap<String, CargoMemberConfig>> {
1233+
self.member_cfgs
1234+
.try_borrow_with(|| Ok(self.get::<HashMap<String, CargoMemberConfig>>("member")?))
1235+
}
1236+
1237+
/// Returns the member config of the given name.
1238+
pub fn member_config(&self, member_name: &str) -> CargoResult<Option<&CargoMemberConfig>> {
1239+
self.member_cfgs().map(|map| map.get(member_name))
1240+
}
1241+
12291242
pub fn crates_io_source_id<F>(&self, f: F) -> CargoResult<SourceId>
12301243
where
12311244
F: FnMut() -> CargoResult<SourceId>,
@@ -1879,6 +1892,12 @@ where
18791892
deserializer.deserialize_option(ProgressVisitor)
18801893
}
18811894

1895+
#[derive(Debug, Deserialize)]
1896+
#[serde(rename_all = "kebab-case")]
1897+
pub struct CargoMemberConfig {
1898+
pub target: Option<ConfigRelativePath>,
1899+
}
1900+
18821901
/// A type to deserialize a list of strings from a toml file.
18831902
///
18841903
/// Supports deserializing either a whitespace-separated list of arguments in a

0 commit comments

Comments
 (0)