Skip to content

Commit

Permalink
Check build target supports std when building with -Zbuild-std=std (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
weihanglo authored Nov 22, 2024
2 parents e444425 + 6a980bc commit 88edf01
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/cargo/core/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pub struct TargetInfo {
crate_types: RefCell<HashMap<CrateType, Option<(String, String)>>>,
/// `cfg` information extracted from `rustc --print=cfg`.
cfg: Vec<Cfg>,
/// `supports_std` information extracted from `rustc --print=target-spec-json`
pub supports_std: Option<bool>,
/// Supported values for `-Csplit-debuginfo=` flag, queried from rustc
support_split_debuginfo: Vec<String>,
/// Path to the sysroot.
Expand Down Expand Up @@ -294,6 +296,44 @@ impl TargetInfo {
gctx.shell().warn("non-trivial mutual dependency between target-specific configuration and RUSTFLAGS")?;
}

let mut supports_std: Option<bool> = 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<bool>,
}

#[derive(Deserialize)]
struct TargetSpec {
pub metadata: Metadata,
}

if let Ok(output) = target_spec_process.output() {
if let Ok(spec) = serde_json::from_slice::<TargetSpec>(&output.stdout) {
supports_std = spec.metadata.std;
}
}
}

return Ok(TargetInfo {
crate_type_process,
crate_types: RefCell::new(map),
Expand All @@ -310,6 +350,7 @@ impl TargetInfo {
)?
.into(),
cfg,
supports_std,
support_split_debuginfo,
});
}
Expand Down Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/cargo/core/compiler/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
26 changes: 26 additions & 0 deletions tests/testsuite/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,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();
Expand Down

0 comments on commit 88edf01

Please sign in to comment.