Skip to content

Commit cd8f9b0

Browse files
committed
Allow artifact target set for build-std
Adds unstable flag `build-std-targets`. The intended use is for projects that mix a Tier 1/2 target as a host with a Tier 3 target in an artifact dependency, which is then deployed at runtime. For instance, x86-64-*-linux and bpfeb-unknown-none, or a supported host with a custom hardware profile given by json. In these cases it is unlikely that all std libraries should be built from scratch. Additionally, the list of std builds are not only those given as direct targets on the command line but rather discovered by target specifications on artifact dependencies that are embedded to a host loader etc. The previous state discovered the set of std-builds through the target list of the CLI but never dynamically within the crate graph. The unit graph would then try to access the std build for kinds that were not generated, resulting in panics in unit dependency planning.
1 parent dfcf4c2 commit cd8f9b0

File tree

3 files changed

+150
-33
lines changed

3 files changed

+150
-33
lines changed

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -174,24 +174,27 @@ fn attach_std_deps(
174174
std_unit_deps: UnitGraph,
175175
) {
176176
// Attach the standard library as a dependency of every target unit.
177-
let mut found = false;
177+
let mut found = HashSet::new();
178178
for (unit, deps) in state.unit_dependencies.iter_mut() {
179179
if !unit.kind.is_host() && !unit.mode.is_run_custom_build() {
180-
deps.extend(std_roots[&unit.kind].iter().map(|unit| UnitDep {
181-
unit: unit.clone(),
182-
unit_for: UnitFor::new_normal(unit.kind),
183-
extern_crate_name: unit.pkg.name(),
184-
dep_name: None,
185-
// TODO: Does this `public` make sense?
186-
public: true,
187-
noprelude: true,
188-
}));
189-
found = true;
180+
if let Some(std_build) = std_roots.get(&unit.kind) {
181+
deps.extend(std_build.iter().map(|unit| UnitDep {
182+
unit: unit.clone(),
183+
unit_for: UnitFor::new_normal(unit.kind),
184+
extern_crate_name: unit.pkg.name(),
185+
dep_name: None,
186+
// TODO: Does this `public` make sense?
187+
public: true,
188+
noprelude: true,
189+
}));
190+
191+
found.insert(unit.kind);
192+
}
190193
}
191194
}
192195
// And also include the dependencies of the standard library itself. Don't
193196
// include these if no units actually needed the standard library.
194-
if found {
197+
if !found.is_empty() {
195198
for (unit, deps) in std_unit_deps.into_iter() {
196199
if let Some(other_unit) = state.unit_dependencies.insert(unit, deps) {
197200
panic!("std unit collision with existing unit: {:?}", other_unit);

src/cargo/core/features.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ use cargo_util::ProcessBuilder;
129129
use serde::{Deserialize, Serialize};
130130

131131
use crate::GlobalContext;
132+
use crate::core::compiler::CompileTarget;
132133
use crate::core::resolver::ResolveBehavior;
133134
use crate::util::errors::CargoResult;
134135
use crate::util::indented_lines;
@@ -824,6 +825,8 @@ unstable_cli_options!(
824825
build_std: Option<Vec<String>> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"),
825826
#[serde(deserialize_with = "deserialize_comma_separated_list")]
826827
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
828+
#[serde(deserialize_with = "deserialize_target_list")]
829+
build_std_targets: Option<Vec<CompileTarget>> = ("Configure a list of targets that cargo should compile the standard library for"),
827830
cargo_lints: bool = ("Enable the `[lints.cargo]` table"),
828831
checksum_freshness: bool = ("Use a checksum to determine if output is fresh rather than filesystem mtime"),
829832
codegen_backend: bool = ("Enable the `codegen-backend` option in profiles in .cargo/config.toml file"),
@@ -1176,6 +1179,32 @@ fn parse_gitoxide(
11761179
Ok(Some(out))
11771180
}
11781181

1182+
fn deserialize_target_list<'de, D>(deserializer: D) -> Result<Option<Vec<CompileTarget>>, D::Error>
1183+
where
1184+
D: serde::Deserializer<'de>,
1185+
{
1186+
use serde::de::Error as _;
1187+
1188+
let targets = deserialize_comma_separated_list(deserializer)?;
1189+
1190+
let Some(targets) = targets else {
1191+
return Ok(None);
1192+
};
1193+
1194+
let targets = targets
1195+
.into_iter()
1196+
.map(|s| match CompileTarget::new(&s) {
1197+
Ok(target) => Ok(target),
1198+
Err(err) => Err(D::Error::custom(err)),
1199+
})
1200+
// Dedup
1201+
.collect::<Result<BTreeSet<_>, D::Error>>()?
1202+
.into_iter()
1203+
.collect::<Vec<_>>();
1204+
1205+
Ok(Some(targets))
1206+
}
1207+
11791208
impl CliUnstable {
11801209
/// Parses `-Z` flags from the command line, and returns messages that warn
11811210
/// if any flag has alreardy been stabilized.
@@ -1336,6 +1365,16 @@ impl CliUnstable {
13361365
"bindeps" => self.bindeps = parse_empty(k, v)?,
13371366
"build-dir" => self.build_dir = parse_empty(k, v)?,
13381367
"build-std" => self.build_std = Some(parse_list(v)),
1368+
"build-std-targets" => {
1369+
self.build_std_targets = Some(
1370+
parse_list(v)
1371+
.into_iter()
1372+
.map(|s| CompileTarget::new(&s))
1373+
.collect::<CargoResult<BTreeSet<_>>>()?
1374+
.into_iter()
1375+
.collect(),
1376+
)
1377+
}
13391378
"build-std-features" => self.build_std_features = Some(parse_list(v)),
13401379
"cargo-lints" => self.cargo_lints = parse_empty(k, v)?,
13411380
"codegen-backend" => self.codegen_backend = parse_empty(k, v)?,

src/cargo/ops/cargo_compile/mod.rs

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use crate::core::compiler::{BuildConfig, BuildContext, BuildRunner, Compilation}
4646
use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData, Unit};
4747
use crate::core::compiler::{CrateType, TargetInfo, apply_env_config, standard_lib};
4848
use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
49+
use crate::core::dependency::ArtifactTarget;
4950
use crate::core::profiles::Profiles;
5051
use crate::core::resolver::features::{self, CliFeatures, FeaturesFor};
5152
use crate::core::resolver::{HasDevUnits, Resolve};
@@ -288,15 +289,37 @@ pub fn create_bcx<'a, 'gctx>(
288289
} = resolve;
289290

290291
let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std {
291-
let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
292-
ws,
293-
&mut target_data,
294-
&build_config,
295-
crates,
296-
&build_config.requested_kinds,
297-
)?;
298-
pkg_set.add_set(std_package_set);
299-
Some((std_resolve, std_features))
292+
let mut required_kinds = build_config.requested_kinds.to_vec();
293+
294+
required_kinds.extend(pkg_set.packages().flat_map(|pkg| {
295+
let manifest = pkg.manifest();
296+
manifest.forced_kind()
297+
}));
298+
299+
if let Some(requested_std) = gctx.cli_unstable().build_std_targets.as_ref() {
300+
required_kinds.retain(|n| match n {
301+
CompileKind::Host => false,
302+
CompileKind::Target(n) => requested_std.contains(n),
303+
});
304+
}
305+
306+
if required_kinds.is_empty() {
307+
tracing::trace!("No host targets requested with build-std");
308+
None
309+
} else {
310+
tracing::trace!("Host targets requested with build-std: {required_kinds:#?}");
311+
312+
let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
313+
ws,
314+
&mut target_data,
315+
&build_config,
316+
crates,
317+
&required_kinds,
318+
)?;
319+
320+
pkg_set.add_set(std_package_set);
321+
Some((std_resolve, std_features))
322+
}
300323
} else {
301324
None
302325
};
@@ -415,18 +438,70 @@ pub fn create_bcx<'a, 'gctx>(
415438
};
416439

417440
let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() {
418-
let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
419-
standard_lib::generate_std_roots(
420-
&crates,
421-
&targeted_root_units,
422-
std_resolve,
423-
std_features,
424-
&explicit_host_kinds,
425-
&pkg_set,
426-
interner,
427-
&profiles,
428-
&target_data,
429-
)?
441+
// Force kinds that require a std build were not collected previously. They must be
442+
// present within `build_unit_dependencies` which then attaches std-deps and their
443+
// std-roots, by target definition.
444+
let mut required_kinds: Vec<_> = packages
445+
.iter()
446+
.flat_map(|pkg| {
447+
let tr = resolve.transitive_deps_not_replaced(pkg.package_id());
448+
tr.filter_map(|(_, dependency)| match dependency.artifact()?.target()? {
449+
ArtifactTarget::BuildDependencyAssumeTarget => None,
450+
ArtifactTarget::Force(target) => Some(target),
451+
})
452+
})
453+
.map(CompileKind::Target)
454+
.collect();
455+
456+
if let Some(requested_std) = gctx.cli_unstable().build_std_targets.as_ref() {
457+
required_kinds.retain(|n| match n {
458+
CompileKind::Host => true,
459+
CompileKind::Target(n) => requested_std.contains(n),
460+
});
461+
}
462+
463+
let custom_targets;
464+
let std_build = if !required_kinds.is_empty() {
465+
let (_std_package_set, std_resolve, std_features) = standard_lib::resolve_std(
466+
ws,
467+
&mut target_data,
468+
&build_config,
469+
crates,
470+
&required_kinds,
471+
)?;
472+
473+
custom_targets = (std_resolve, std_features);
474+
// to_builds.extend(std_package_set.packages());
475+
Some(&custom_targets)
476+
} else {
477+
std_resolve_features.as_ref()
478+
};
479+
480+
let target_kinds = if !required_kinds.is_empty() {
481+
tracing::trace!("Build-std artifact targets for {specs:?}: {required_kinds:#?}");
482+
&required_kinds
483+
} else {
484+
tracing::trace!("Build-std host for {specs:?}: {explicit_host_kinds:#?}");
485+
&explicit_host_kinds
486+
};
487+
488+
if let Some((std_resolve, std_features)) = std_build {
489+
tracing::trace!("Build std targets for {specs:?}: {required_kinds:?}");
490+
491+
standard_lib::generate_std_roots(
492+
&crates,
493+
&targeted_root_units,
494+
std_resolve,
495+
std_features,
496+
target_kinds,
497+
&pkg_set,
498+
interner,
499+
&profiles,
500+
&target_data,
501+
)?
502+
} else {
503+
Default::default()
504+
}
430505
} else {
431506
Default::default()
432507
};

0 commit comments

Comments
 (0)