From ee0850654b4cda6ccd9856762f867ba28a6d32f3 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Wed, 24 Jun 2026 07:37:11 -0700 Subject: [PATCH 1/2] test(build-std): cover extra target kinds --- tests/testsuite/standard_lib.rs | 145 ++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index acccdd4699d..146817f51a2 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -7,6 +7,7 @@ use std::path::{Path, PathBuf}; use crate::prelude::*; +use crate::utils::cross_compile::disabled as cross_compile_disabled; use cargo_test_support::ProjectBuilder; use cargo_test_support::cross_compile; use cargo_test_support::registry::{Dependency, Package}; @@ -518,6 +519,150 @@ fn depend_same_as_std() { p.cargo("build -v").build_std(&setup).target_host().run(); } +#[cargo_test(build_std_mock)] +fn artifact_dep_target_builds_std_for_unrequested_target() { + if cross_compile_disabled() { + return; + } + let target = cross_compile::alternate(); + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + resolver = "2" + + [dependencies] + bindep = {{ path = "bindep", artifact = "bin", target = "{target}" }} + "#, + ), + ) + .file( + "src/lib.rs", + r#" + pub fn foo() { + std::custom_api(); + let _bin = include_bytes!(env!("CARGO_BIN_FILE_BINDEP")); + } + "#, + ) + .file( + "bindep/Cargo.toml", + r#" + [package] + name = "bindep" + version = "0.0.1" + edition = "2021" + "#, + ) + .file( + "bindep/src/main.rs", + r#" + fn main() { + std::custom_api(); + } + "#, + ) + .build(); + + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .build_std(&setup) + .target_host() + .with_status(101) + .with_stderr_contains("no entry found for key") + .run(); +} + +#[cargo_test(build_std_mock)] +fn inactive_artifact_dep_target_does_not_build_std() { + if cross_compile_disabled() { + return; + } + let target = cross_compile::alternate(); + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + resolver = "2" + + [dependencies] + bindep = {{ path = "bindep", artifact = "bin", target = "{target}", optional = true }} + "#, + ), + ) + .file("src/lib.rs", "pub fn foo() { std::custom_api(); }") + .file( + "bindep/Cargo.toml", + r#" + [package] + name = "bindep" + version = "0.0.1" + edition = "2021" + "#, + ) + .file("bindep/src/main.rs", "fn main() { std::custom_api(); }") + .build(); + + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .build_std(&setup) + .target_host() + .with_stderr_does_not_contain(str![[r#" +[RUNNING] `[..]rustc --crate-name std [..]--target [ALT_TARGET][..]` +"#]]) + .run(); +} + +#[cargo_test(build_std_mock)] +fn per_package_target_builds_std_for_manifest_target() { + if cross_compile_disabled() { + return; + } + let target = cross_compile::alternate(); + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + default-target = "{target}" + "#, + ), + ) + .file("src/lib.rs", "pub fn foo() { std::custom_api(); }") + .build(); + + let mut cargo = p.cargo("check -v"); + enable_build_std(&mut cargo, &setup); + cargo + .arg("-Zbuild-std") + .masquerade_as_nightly_cargo(&["build-std", "per-package-target"]) + .with_status(101) + .with_stderr_contains("no entry found for key") + .run(); +} + #[cargo_test(build_std_mock)] fn test() { let setup = setup(); From cebeb7bfa698c7fcbae1eee8cfdbec1907887f29 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Wed, 24 Jun 2026 07:38:23 -0700 Subject: [PATCH 2/2] fix(build-std): include resolved extra target kinds --- .../compiler/build_context/target_info.rs | 43 ++++++++++------ src/cargo/core/compiler/standard_lib.rs | 51 +++++++++++++++++-- src/cargo/ops/cargo_compile/mod.rs | 40 ++++++++++----- src/cargo/ops/cargo_fetch.rs | 10 ++-- tests/testsuite/standard_lib.rs | 18 +++++-- 5 files changed, 117 insertions(+), 45 deletions(-) diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index d6122f6e4b6..6a463acbb02 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -959,6 +959,7 @@ pub struct RustcTargetData<'gctx> { target_config: HashMap, /// Information about the target platform that we're building for. target_info: HashMap, + manifest_target_kinds: Vec, } impl<'gctx> RustcTargetData<'gctx> { @@ -1005,16 +1006,6 @@ impl<'gctx> RustcTargetData<'gctx> { } }; - let mut res = RustcTargetData { - rustc, - gctx, - requested_kinds: requested_kinds.into(), - host_config, - host_info, - target_config, - target_info, - }; - // Get all kinds we currently know about. // // For now, targets can only ever come from the root workspace @@ -1029,16 +1020,32 @@ impl<'gctx> RustcTargetData<'gctx> { .iter() .filter_map(|d| d.artifact()?.target()?.to_compile_kind()) } - let all_kinds = requested_kinds - .iter() - .copied() - .chain(ws.members().flat_map(|p| { + let mut manifest_target_kinds = ws + .members() + .flat_map(|p| { p.manifest() .default_kind() .into_iter() .chain(p.manifest().forced_kind()) - .chain(artifact_targets(p)) - })); + }) + .collect::>(); + manifest_target_kinds.sort(); + manifest_target_kinds.dedup(); + let mut res = RustcTargetData { + rustc, + gctx, + requested_kinds: requested_kinds.into(), + host_config, + host_info, + target_config, + target_info, + manifest_target_kinds: manifest_target_kinds.clone(), + }; + let all_kinds = requested_kinds + .iter() + .copied() + .chain(manifest_target_kinds.iter().copied()) + .chain(ws.members().flat_map(artifact_targets)); for kind in all_kinds { res.merge_compile_kind(kind)?; } @@ -1130,4 +1137,8 @@ impl<'gctx> RustcTargetData<'gctx> { pub fn requested_kinds(&self) -> &[CompileKind] { &self.requested_kinds } + + pub fn manifest_target_kinds(&self) -> &[CompileKind] { + &self.manifest_target_kinds + } } diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 45a313a921d..9995bdbb729 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -13,8 +13,6 @@ use crate::util::errors::CargoResult; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; -use super::BuildConfig; - fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> { let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str())); // This is a temporary hack until there is a more principled way to @@ -49,7 +47,6 @@ fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) - pub fn resolve_std<'gctx>( ws: &Workspace<'gctx>, target_data: &mut RustcTargetData<'gctx>, - build_config: &BuildConfig, crates: &[String], kinds: &[CompileKind], ) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> { @@ -93,7 +90,7 @@ pub fn resolve_std<'gctx>( let mut resolve = ops::resolve_ws_with_opts( &std_ws, target_data, - &build_config.requested_kinds, + kinds, &cli_features, &specs, HasDevUnits::No, @@ -112,6 +109,52 @@ pub fn resolve_std<'gctx>( )) } +pub fn resolve_std_kinds<'gctx>( + target_data: &mut RustcTargetData<'gctx>, + requested_kinds: &[CompileKind], + resolve: &Resolve, +) -> CargoResult> { + let host_kind_requested = requested_kinds.iter().any(CompileKind::is_host); + let mut kinds = Vec::new(); + + // With no explicit `--target`, Cargo represents the root package as + // `CompileKind::Host` but internally also loads target data for the host + // triple. Avoid returning both, otherwise build-std would resolve and + // generate duplicate std roots for the same platform. + let mut add_kind = + |target_data: &mut RustcTargetData<'gctx>, kind: CompileKind| -> CargoResult<()> { + if host_kind_requested + && matches!(kind, CompileKind::Target(_)) + && target_data.rustc.host == target_data.short_name(&kind) + { + return Ok(()); + } + + target_data.merge_compile_kind(kind)?; + kinds.push(kind); + Ok(()) + }; + + for kind in requested_kinds.iter().copied() { + add_kind(target_data, kind)?; + } + for kind in target_data.manifest_target_kinds().to_vec() { + add_kind(target_data, kind)?; + } + for kind in resolve.iter().flat_map(|pkg_id| { + resolve + .deps(pkg_id) + .flat_map(|(_, deps)| deps.iter()) + .filter_map(|dep| dep.artifact()?.target()?.to_compile_kind()) + }) { + add_kind(target_data, kind)?; + } + + kinds.sort(); + kinds.dedup(); + Ok(kinds) +} + /// Generates a map of root units for the standard library for each kind requested. /// /// * `crates` is the arg value from `-Zbuild-std`. diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index ba6c433fcfe..e41b441e0e2 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -339,19 +339,20 @@ pub fn create_bcx<'a, 'gctx>( logger.log(LogMessage::ResolutionFinished { elapsed }); } - let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std { - let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std( - ws, - &mut target_data, - &build_config, - crates, - &build_config.requested_kinds, - )?; - pkg_set.add_set(std_package_set); - Some((std_resolve, std_features)) - } else { - None - }; + let (std_resolve_features, build_std_kinds) = + if let Some(crates) = &gctx.cli_unstable().build_std { + let std_kinds = standard_lib::resolve_std_kinds( + &mut target_data, + &build_config.requested_kinds, + &resolve, + )?; + let (std_package_set, std_resolve, std_features) = + standard_lib::resolve_std(ws, &mut target_data, crates, &std_kinds)?; + pkg_set.add_set(std_package_set); + (Some((std_resolve, std_features)), Some(std_kinds)) + } else { + (None, None) + }; // Find the packages in the resolver that the user wants to build (those // passed in with `-p` or the defaults from the workspace), and convert @@ -479,12 +480,23 @@ pub fn create_bcx<'a, 'gctx>( let std_roots = if let Some(crates) = gctx.cli_unstable().build_std.as_ref() { let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap(); + let mut std_kinds = explicit_host_kinds.clone(); + std_kinds.extend( + build_std_kinds + .as_ref() + .unwrap() + .iter() + .copied() + .filter(|kind| !kind.is_host()), + ); + std_kinds.sort(); + std_kinds.dedup(); standard_lib::generate_std_roots( &crates, &targeted_root_units, std_resolve, std_features, - &explicit_host_kinds, + &std_kinds, &pkg_set, interner, &profiles, diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index 5742556215e..62ce8e86c24 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -71,13 +71,9 @@ pub fn fetch<'a>( // If -Zbuild-std was passed, download dependencies for the standard library. if let Some(crates) = &gctx.cli_unstable().build_std { - let (std_package_set, _, _) = standard_lib::resolve_std( - ws, - &mut data, - &build_config, - crates, - &build_config.requested_kinds, - )?; + let std_kinds = + standard_lib::resolve_std_kinds(&mut data, &build_config.requested_kinds, &resolve)?; + let (std_package_set, _, _) = standard_lib::resolve_std(ws, &mut data, crates, &std_kinds)?; packages.add_set(std_package_set); } diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index 146817f51a2..309e5c8ef6e 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -575,8 +575,13 @@ fn artifact_dep_target_builds_std_for_unrequested_target() { .masquerade_as_nightly_cargo(&["bindeps"]) .build_std(&setup) .target_host() - .with_status(101) - .with_stderr_contains("no entry found for key") + .with_stderr_data(str![[r#" +... +[RUNNING] `[..]rustc --crate-name std [..]--target [ALT_TARGET][..]` +... +[RUNNING] `[..]rustc --crate-name bindep [..]--target [ALT_TARGET][..]` +... +"#]]) .run(); } @@ -658,8 +663,13 @@ fn per_package_target_builds_std_for_manifest_target() { cargo .arg("-Zbuild-std") .masquerade_as_nightly_cargo(&["build-std", "per-package-target"]) - .with_status(101) - .with_stderr_contains("no entry found for key") + .with_stderr_data(str![[r#" +... +[RUNNING] `[..]rustc --crate-name std [..]--target [ALT_TARGET][..]` +... +[RUNNING] `[..]rustc --crate-name foo [..]--target [ALT_TARGET][..]` +... +"#]]) .run(); }