diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index f36fc173bcc0..79ecf5785989 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. @@ -56,6 +58,16 @@ pub struct TargetInfo { pub rustdocflags: Rc<[String]>, } +#[derive(Deserialize)] +pub struct Metadata { + pub std: Option, +} + +#[derive(Deserialize)] +pub struct TargetSpec { + pub metadata: Metadata, +} + /// Kind of each file generated by a Unit, part of `FileType`. #[derive(Clone, PartialEq, Eq, Debug)] pub enum FileFlavor { @@ -294,6 +306,42 @@ 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.nightly_features_allowed && 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()); + } + + let target_spec_cached_output = + rustc.cached_output(&target_spec_process, extra_fingerprint); + + if target_spec_cached_output.is_ok() { + let (stdout, _stderr) = target_spec_cached_output.unwrap(); + match serde_json::from_str(&stdout) { + Result::Ok(val) => { + let target_spec: TargetSpec = val; + supports_std = target_spec.metadata.std; + } + Result::Err(_) => (), + } + } + } + return Ok(TargetInfo { crate_type_process, crate_types: RefCell::new(map), @@ -310,6 +358,7 @@ impl TargetInfo { )? .into(), cfg, + supports_std, support_split_debuginfo, }); } @@ -1026,6 +1075,11 @@ impl<'gctx> RustcTargetData<'gctx> { CompileKind::Target(s) => &self.target_config[&s], } } + + /// Gets the target info hashmap from the target data. + pub fn target_info(&self) -> &HashMap { + &self.target_info + } } /// 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 a3b2ff8acd19..83a61e8211a3 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -72,6 +72,18 @@ 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()) { + for (target, target_info) in target_data.target_info() { + if target_info.supports_std == Some(false) { + anyhow::bail!( + "building std is not supported on this target: {:?}", + target.short_name() + ); + } + } + } + let src_path = detect_sysroot_src_path(target_data)?; let std_ws_manifest_path = src_path.join("Cargo.toml"); let gctx = ws.gctx();