From 5e4a1db935cbc1e1bb24ab0e48f0ba0e9d79aeb8 Mon Sep 17 00:00:00 2001 From: Harry Moulton Date: Fri, 31 May 2024 15:40:18 +0100 Subject: [PATCH 1/2] Check build target supports std when building with -Zbuild-std=std Running cargo with "-Zbuild-std=std" should check whether the build target supports building the standard library. This information can be obtained from rustc with the target-spec-json "--print" option. When 'std' is false for the build target, cargo should not attempt to build the standard library. This avoids the "use of unstable library" errors, as this check is performed before Cargo starts trying to build restricted_std code. Cargo will now emit a warning if the requested target does not support building the standard library, or if there was an issue when collecting the necessary information via rustc --- .../compiler/build_context/target_info.rs | 51 +++++++++++++++++++ src/cargo/core/compiler/standard_lib.rs | 11 ++++ 2 files changed, 62 insertions(+) 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(); From 6a980bca738923c9cee99f211fd2123858a7d44f Mon Sep 17 00:00:00 2001 From: Harry Moulton Date: Fri, 31 May 2024 15:44:23 +0100 Subject: [PATCH 2/2] Add new test case 'test_std_on_unsupported_target' Add a new test case to check cargo handles building a target which doesn't support the standard library properly. --- tests/testsuite/standard_lib.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) 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();