diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index f36fc173bcc..2efae88f1ce 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -43,6 +43,8 @@ pub struct TargetInfo { crate_types: RefCell>>, /// `cfg` information extracted from `rustc --print=cfg`. cfg: Vec, + /// `supports_std` information extracted from `rustc --print=target-spec-json` + pub supports_std: Option, /// Supported values for `-Csplit-debuginfo=` flag, queried from rustc support_split_debuginfo: Vec, /// Path to the sysroot. @@ -294,6 +296,44 @@ impl TargetInfo { gctx.shell().warn("non-trivial mutual dependency between target-specific configuration and RUSTFLAGS")?; } + let mut supports_std: Option = None; + + // The '--print=target-spec-json' is an unstable option of rustc, therefore only + // try to fetch this information if rustc allows nightly features. Additionally, + // to avoid making two rustc queries when not required, only try to fetch the + // target-spec when the '-Zbuild-std' option is passed. + if gctx.cli_unstable().build_std.is_some() { + let mut target_spec_process = rustc.workspace_process(); + apply_env_config(gctx, &mut target_spec_process)?; + target_spec_process + .arg("--print=target-spec-json") + .arg("-Zunstable-options") + .args(&rustflags) + .env_remove("RUSTC_LOG"); + + if let CompileKind::Target(target) = kind { + target_spec_process + .arg("--target") + .arg(target.rustc_target()); + } + + #[derive(Deserialize)] + struct Metadata { + pub std: Option, + } + + #[derive(Deserialize)] + struct TargetSpec { + pub metadata: Metadata, + } + + if let Ok(output) = target_spec_process.output() { + if let Ok(spec) = serde_json::from_slice::(&output.stdout) { + supports_std = spec.metadata.std; + } + } + } + return Ok(TargetInfo { crate_type_process, crate_types: RefCell::new(map), @@ -310,6 +350,7 @@ impl TargetInfo { )? .into(), cfg, + supports_std, support_split_debuginfo, }); } @@ -1026,6 +1067,16 @@ impl<'gctx> RustcTargetData<'gctx> { CompileKind::Target(s) => &self.target_config[&s], } } + + pub fn get_unsupported_std_targets(&self) -> Vec<&str> { + let mut unsupported = Vec::new(); + for (target, target_info) in &self.target_info { + if target_info.supports_std == Some(false) { + unsupported.push(target.short_name()); + } + } + unsupported + } } /// Structure used to deal with Rustdoc fingerprinting diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index a3b2ff8acd1..441d970715d 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -72,6 +72,17 @@ pub fn resolve_std<'gctx>( .warn("-Zbuild-std does not currently fully support --build-plan")?; } + // check that targets support building std + if crates.contains(&"std".to_string()) { + let unsupported_targets = target_data.get_unsupported_std_targets(); + if !unsupported_targets.is_empty() { + anyhow::bail!( + "building std is not supported on the following targets: {}", + unsupported_targets.join(", ") + ) + } + } + let src_path = detect_sysroot_src_path(target_data)?; let std_ws_manifest_path = src_path.join("Cargo.toml"); let gctx = ws.gctx(); diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index 08d371e2fdb..7c3eb35f1af 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -323,6 +323,32 @@ fn check_core() { .run(); } +#[cargo_test(build_std_mock)] +fn test_std_on_unsupported_target() { + let setup = setup(); + + let p = project() + .file( + "src/main.rs", + r#" + fn main() { + println!("hello"); + } + "#, + ) + .build(); + + p.cargo("build") + .arg("--target=aarch64-unknown-none") + .arg("--target=x86_64-unknown-none") + .build_std(&setup) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] building std is not supported on the following targets: [..] +"#]]) + .run(); +} + #[cargo_test(build_std_mock)] fn depend_same_as_std() { let setup = setup();