Skip to content

Commit 88edf01

Browse files
authored
Check build target supports std when building with -Zbuild-std=std (#14183)
**What does this PR try to resolve?** Ensures that Cargo first verifies whether a given target supports building the standard library when the `-Zbuild-std=std` option is passed to Cargo ([see issue here](rust-lang/wg-cargo-std-aware#87)). This information can be obtained by querying `rustc --print=target-spec-json`. The target spec "metadata" contains an `Option<bool>` determining whether the target supports building std. In the case this value is `false`, cargo will stop the build. This avoids the numerous "use of unstable library" errors, giving a cleaner, and simpler, "building std is not supported on this target". **How should we test and review this PR?** It can be manually tested by running `cargo build --target <target> -Zbuild-std=std`. If a target who's target-spec marks std as false is passed, cargo will exit. This works with multiple `--target`'s, and if any of them don't support std, cargo will exit. **Additional Information** This change relies on two things: * The target-spec metadata in rustc needs to be filled out. Currently, most fields are None, which is treated as OK with this change. Meaning this can be merged before the rustc changes. * The new test case added with this change will fail without at least `aarch64-unknown-none` having it's target-spec metadata completed. * Some targets have std support marked as "?". If this state is properly represented in the target-spec in the future, it needs to be determined whether this is allowed, so the build can continue, or whether it fails.
2 parents e444425 + 6a980bc commit 88edf01

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

src/cargo/core/compiler/build_context/target_info.rs

+51
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub struct TargetInfo {
4343
crate_types: RefCell<HashMap<CrateType, Option<(String, String)>>>,
4444
/// `cfg` information extracted from `rustc --print=cfg`.
4545
cfg: Vec<Cfg>,
46+
/// `supports_std` information extracted from `rustc --print=target-spec-json`
47+
pub supports_std: Option<bool>,
4648
/// Supported values for `-Csplit-debuginfo=` flag, queried from rustc
4749
support_split_debuginfo: Vec<String>,
4850
/// Path to the sysroot.
@@ -294,6 +296,44 @@ impl TargetInfo {
294296
gctx.shell().warn("non-trivial mutual dependency between target-specific configuration and RUSTFLAGS")?;
295297
}
296298

299+
let mut supports_std: Option<bool> = None;
300+
301+
// The '--print=target-spec-json' is an unstable option of rustc, therefore only
302+
// try to fetch this information if rustc allows nightly features. Additionally,
303+
// to avoid making two rustc queries when not required, only try to fetch the
304+
// target-spec when the '-Zbuild-std' option is passed.
305+
if gctx.cli_unstable().build_std.is_some() {
306+
let mut target_spec_process = rustc.workspace_process();
307+
apply_env_config(gctx, &mut target_spec_process)?;
308+
target_spec_process
309+
.arg("--print=target-spec-json")
310+
.arg("-Zunstable-options")
311+
.args(&rustflags)
312+
.env_remove("RUSTC_LOG");
313+
314+
if let CompileKind::Target(target) = kind {
315+
target_spec_process
316+
.arg("--target")
317+
.arg(target.rustc_target());
318+
}
319+
320+
#[derive(Deserialize)]
321+
struct Metadata {
322+
pub std: Option<bool>,
323+
}
324+
325+
#[derive(Deserialize)]
326+
struct TargetSpec {
327+
pub metadata: Metadata,
328+
}
329+
330+
if let Ok(output) = target_spec_process.output() {
331+
if let Ok(spec) = serde_json::from_slice::<TargetSpec>(&output.stdout) {
332+
supports_std = spec.metadata.std;
333+
}
334+
}
335+
}
336+
297337
return Ok(TargetInfo {
298338
crate_type_process,
299339
crate_types: RefCell::new(map),
@@ -310,6 +350,7 @@ impl TargetInfo {
310350
)?
311351
.into(),
312352
cfg,
353+
supports_std,
313354
support_split_debuginfo,
314355
});
315356
}
@@ -1026,6 +1067,16 @@ impl<'gctx> RustcTargetData<'gctx> {
10261067
CompileKind::Target(s) => &self.target_config[&s],
10271068
}
10281069
}
1070+
1071+
pub fn get_unsupported_std_targets(&self) -> Vec<&str> {
1072+
let mut unsupported = Vec::new();
1073+
for (target, target_info) in &self.target_info {
1074+
if target_info.supports_std == Some(false) {
1075+
unsupported.push(target.short_name());
1076+
}
1077+
}
1078+
unsupported
1079+
}
10291080
}
10301081

10311082
/// Structure used to deal with Rustdoc fingerprinting

src/cargo/core/compiler/standard_lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ pub fn resolve_std<'gctx>(
7272
.warn("-Zbuild-std does not currently fully support --build-plan")?;
7373
}
7474

75+
// check that targets support building std
76+
if crates.contains(&"std".to_string()) {
77+
let unsupported_targets = target_data.get_unsupported_std_targets();
78+
if !unsupported_targets.is_empty() {
79+
anyhow::bail!(
80+
"building std is not supported on the following targets: {}",
81+
unsupported_targets.join(", ")
82+
)
83+
}
84+
}
85+
7586
let src_path = detect_sysroot_src_path(target_data)?;
7687
let std_ws_manifest_path = src_path.join("Cargo.toml");
7788
let gctx = ws.gctx();

tests/testsuite/standard_lib.rs

+26
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,32 @@ fn check_core() {
389389
.run();
390390
}
391391

392+
#[cargo_test(build_std_mock)]
393+
fn test_std_on_unsupported_target() {
394+
let setup = setup();
395+
396+
let p = project()
397+
.file(
398+
"src/main.rs",
399+
r#"
400+
fn main() {
401+
println!("hello");
402+
}
403+
"#,
404+
)
405+
.build();
406+
407+
p.cargo("build")
408+
.arg("--target=aarch64-unknown-none")
409+
.arg("--target=x86_64-unknown-none")
410+
.build_std(&setup)
411+
.with_status(101)
412+
.with_stderr_data(str![[r#"
413+
[ERROR] building std is not supported on the following targets: [..]
414+
"#]])
415+
.run();
416+
}
417+
392418
#[cargo_test(build_std_mock)]
393419
fn depend_same_as_std() {
394420
let setup = setup();

0 commit comments

Comments
 (0)