Rollup merge of #150524 - test-build-std, r=jieyouxu,kobzol

Test that -Zbuild-std=core works on a variety of profiles

See [#t-infra > Non-blocking testing for -Zbuild-std?](https://rust-lang.zulipchat.com/#narrow/channel/242791-t-infra/topic/Non-blocking.20testing.20for.20-Zbuild-std.3F/with/565837190) for some background.

This is an incredibly CPU-hungry run-make-cargo test, but at least on my desktop the entire suite only takes a minute.
This commit is contained in:
Guillaume Gomez
2026-01-20 14:36:31 +01:00
committed by GitHub
21 changed files with 192 additions and 11 deletions

View File

@@ -3337,6 +3337,7 @@ dependencies = [
"rustdoc-json-types",
"serde_json",
"similar",
"tempfile",
"wasmparser 0.236.1",
]

View File

@@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
Target {
llvm_target: "i586-unknown-redox".into(),
metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None },
metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None },
pointer_width: 32,
data_layout:
"e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128"

View File

@@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
llvm_target: "x86_64-unknown-linux-none".into(),
metadata: TargetMetadata {
description: None,
tier: None,
tier: Some(3),
host_tools: None,
std: Some(false),
},

View File

@@ -9,7 +9,7 @@ pub(crate) fn target() -> Target {
pointer_width: 32,
data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(),
arch: Arch::Xtensa,
metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None },
metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None },
options: TargetOptions {
endian: Endian::Little,

View File

@@ -9,7 +9,7 @@ pub(crate) fn target() -> Target {
pointer_width: 32,
data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(),
arch: Arch::Xtensa,
metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None },
metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None },
options: TargetOptions {
endian: Endian::Little,

View File

@@ -9,7 +9,7 @@ pub(crate) fn target() -> Target {
pointer_width: 32,
data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(),
arch: Arch::Xtensa,
metadata: TargetMetadata { description: None, tier: None, host_tools: None, std: None },
metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None },
options: TargetOptions {
endian: Endian::Little,

View File

@@ -53,6 +53,11 @@ check-aux:
src/tools/cargotest \
src/tools/test-float-parse \
$(BOOTSTRAP_ARGS)
# The build-std suite is off by default because it is uncommonly slow
# and memory-hungry.
$(Q)$(BOOTSTRAP) test --stage 2 \
build-std \
$(BOOTSTRAP_ARGS)
# Run standard library tests in Miri.
$(Q)MIRIFLAGS="-Zmiri-strict-provenance" \
$(BOOTSTRAP) miri --stage 2 \

View File

@@ -1625,6 +1625,12 @@ test!(RunMakeCargo {
suite: "run-make-cargo",
default: true
});
test!(BuildStd {
path: "tests/build-std",
mode: CompiletestMode::RunMake,
suite: "build-std",
default: false
});
test!(AssemblyLlvm {
path: "tests/assembly-llvm",
@@ -1948,7 +1954,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
let stage0_rustc_path = builder.compiler(0, test_compiler.host);
cmd.arg("--stage0-rustc-path").arg(builder.rustc(stage0_rustc_path));
if suite == "run-make-cargo" {
if matches!(suite, "run-make-cargo" | "build-std") {
let cargo_path = if test_compiler.stage == 0 {
// If we're using `--stage 0`, we should provide the bootstrap cargo.
builder.initial_cargo.clone()

View File

@@ -20,6 +20,7 @@ pub(crate) const PATH_REMAP: &[(&str, &[&str])] = &[
&[
// tidy-alphabetical-start
"tests/assembly-llvm",
"tests/build-std",
"tests/codegen-llvm",
"tests/codegen-units",
"tests/coverage",

View File

@@ -5,6 +5,9 @@ expression: test tests
[Test] test::AssemblyLlvm
targets: [aarch64-unknown-linux-gnu]
- Suite(test::tests/assembly-llvm)
[Test] test::BuildStd
targets: [aarch64-unknown-linux-gnu]
- Suite(test::tests/build-std)
[Test] test::CodegenLlvm
targets: [aarch64-unknown-linux-gnu]
- Suite(test::tests/codegen-llvm)

View File

@@ -5,6 +5,9 @@ expression: test tests --skip=coverage
[Test] test::AssemblyLlvm
targets: [aarch64-unknown-linux-gnu]
- Suite(test::tests/assembly-llvm)
[Test] test::BuildStd
targets: [aarch64-unknown-linux-gnu]
- Suite(test::tests/build-std)
[Test] test::CodegenLlvm
targets: [aarch64-unknown-linux-gnu]
- Suite(test::tests/codegen-llvm)

View File

@@ -927,6 +927,7 @@ impl<'a> Builder<'a> {
test::CollectLicenseMetadata,
test::RunMake,
test::RunMakeCargo,
test::BuildStd,
),
Kind::Miri => describe!(test::Crate),
Kind::Bench => describe!(test::Crate, test::CrateLibrustc, test::CrateRustdoc),

View File

@@ -2077,7 +2077,7 @@ mod snapshot {
let ctx = TestCtx::new();
insta::assert_snapshot!(
prepare_test_config(&ctx)
.render_steps(), @r"
.render_steps(), @"
[build] rustc 0 <host> -> Tidy 1 <host>
[test] tidy <>
[build] rustdoc 0 <host>
@@ -2255,7 +2255,7 @@ mod snapshot {
insta::assert_snapshot!(
prepare_test_config(&ctx)
.stage(2)
.render_steps(), @r"
.render_steps(), @"
[build] rustc 0 <host> -> Tidy 1 <host>
[test] tidy <>
[build] rustdoc 0 <host>

View File

@@ -78,6 +78,10 @@ The following test suites are available, with links for more information:
[`run-make`](#run-make-tests) are general purpose tests using Rust programs.
### The build-std test suite
[`build-std`](#build-std-tests) test that -Zbuild-std works.
### Rustdoc test suites
| Test suite | Purpose |
@@ -429,6 +433,12 @@ use cases that require testing in-tree `cargo` in conjunction with in-tree `rust
The `run-make` test suite does not have access to in-tree `cargo` (so it can be the
faster-to-iterate test suite).
### `build-std` tests
The tests in [`tests/build-std`] check that `-Zbuild-std` works. This is currently
just a run-make test suite with a single recipe. The recipe generates test cases
and runs them in parallel.
#### Using Rust recipes
Each test should be in a separate directory with a `rmake.rs` Rust program,

View File

@@ -77,6 +77,7 @@ string_enum! {
RustdocUi => "rustdoc-ui",
Ui => "ui",
UiFullDeps => "ui-fulldeps",
BuildStd => "build-std",
}
}

View File

@@ -190,8 +190,8 @@ impl TestCx<'_> {
// through a specific CI runner).
.env("LLVM_COMPONENTS", &self.config.llvm_components);
// Only `run-make-cargo` test suite gets an in-tree `cargo`, not `run-make`.
if self.config.suite == TestSuite::RunMakeCargo {
// The `run-make-cargo` and `build-std` suites need an in-tree `cargo`, `run-make` does not.
if matches!(self.config.suite, TestSuite::RunMakeCargo | TestSuite::BuildStd) {
cmd.env(
"CARGO",
self.config.cargo_path.as_ref().expect("cargo must be built and made available"),

View File

@@ -17,6 +17,7 @@ object = "0.37"
regex = "1.11"
serde_json = "1.0"
similar = "2.7"
tempfile = "3"
wasmparser = { version = "0.236", default-features = false, features = ["std", "features", "validate"] }
# tidy-alphabetical-end

View File

@@ -46,6 +46,8 @@ pub struct Command {
// Emulate linear type semantics.
drop_bomb: DropBomb,
already_executed: bool,
context: String,
}
impl Command {
@@ -60,6 +62,7 @@ impl Command {
stdout: None,
stderr: None,
already_executed: false,
context: String::new(),
}
}
@@ -69,6 +72,16 @@ impl Command {
self.cmd
}
pub(crate) fn get_context(&self) -> &str {
&self.context
}
/// Appends context to the command, to provide a better error message if the command fails.
pub fn context(&mut self, ctx: &str) -> &mut Self {
self.context.push_str(&format!("{ctx}\n"));
self
}
/// Specify a stdin input buffer. This is a convenience helper,
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.stdin_buf = Some(input.as_ref().to_vec().into_boxed_slice());

View File

@@ -34,7 +34,9 @@ pub mod rfs {
}
// Re-exports of third-party library crates.
pub use {bstr, gimli, libc, object, regex, rustdoc_json_types, serde_json, similar, wasmparser};
pub use {
bstr, gimli, libc, object, regex, rustdoc_json_types, serde_json, similar, tempfile, wasmparser,
};
// Helpers for building names of output artifacts that are potentially target-specific.
pub use crate::artifact_names::{

View File

@@ -21,6 +21,9 @@ pub(crate) fn handle_failed_output(
eprintln!("output status: `{}`", output.status());
eprintln!("=== STDOUT ===\n{}\n\n", output.stdout_utf8());
eprintln!("=== STDERR ===\n{}\n\n", output.stderr_utf8());
if !cmd.get_context().is_empty() {
eprintln!("Context:\n{}", cmd.get_context());
}
std::process::exit(1)
}

View File

@@ -0,0 +1,131 @@
// This test ensures we are able to compile -Zbuild-std=core under a variety of profiles.
// Currently, it tests that we can compile to all Tier 1 targets, and it does this by checking what
// the tier metadata in target-spec JSON. This means that all in-tree targets must have a tier set.
#![deny(warnings)]
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use run_make_support::serde_json::{self, Value};
use run_make_support::tempfile::TempDir;
use run_make_support::{cargo, rfs, rustc};
#[derive(Clone)]
struct Task {
target: String,
opt_level: u8,
debug: u8,
panic: &'static str,
}
fn manifest(task: &Task) -> String {
let Task { opt_level, debug, panic, target: _ } = task;
format!(
r#"[package]
name = "scratch"
version = "0.1.0"
edition = "2024"
[lib]
path = "lib.rs"
[profile.release]
opt-level = {opt_level}
debug = {debug}
panic = "{panic}"
"#
)
}
fn main() {
let mut targets = Vec::new();
let all_targets =
rustc().args(&["--print=all-target-specs-json", "-Zunstable-options"]).run().stdout_utf8();
let all_targets: HashMap<String, Value> = serde_json::from_str(&all_targets).unwrap();
for (target, spec) in all_targets {
let metadata = spec.as_object().unwrap()["metadata"].as_object().unwrap();
let tier = metadata["tier"]
.as_u64()
.expect(&format!("Target {} is missing tier metadata", target));
if tier == 1 {
targets.push(target);
}
}
let mut tasks = Vec::new();
// Testing every combination of compiler flags is infeasible. So we are making some attempt to
// choose combinations that will tend to run into problems.
//
// The particular combination of settings below is tuned to look for problems generating the
// code for compiler-builtins.
// We only exercise opt-level 0 and 3 to exercise mir-opt-level 1 and 2.
// We only exercise debug 0 and 2 because level 2 turns off some MIR optimizations.
// We only test abort and immediate-abort because abort vs unwind doesn't change MIR much at
// all. but immediate-abort does.
//
// Currently this only tests that we can compile the tier 1 targets. But since we are using
// -Zbuild-std=core, we could have any list of targets.
for opt_level in [0, 3] {
for debug in [0, 2] {
for panic in ["abort", "immediate-abort"] {
for target in &targets {
tasks.push(Task { target: target.clone(), opt_level, debug, panic });
}
}
}
}
let tasks = Arc::new(Mutex::new(tasks));
let mut threads = Vec::new();
// Try to obey the -j argument passed to bootstrap, otherwise fall back to using all the system
// resouces. This test can be rather memory-hungry (~1 GB/thread); if it causes trouble in
// practice do not hesitate to limit its parallelism.
for _ in 0..run_make_support::env::jobs() {
let tasks = Arc::clone(&tasks);
let handle = thread::spawn(move || {
loop {
let maybe_task = tasks.lock().unwrap().pop();
if let Some(task) = maybe_task {
test(task);
} else {
break;
}
}
});
threads.push(handle);
}
for t in threads {
t.join().unwrap();
}
}
fn test(task: Task) {
let dir = TempDir::new().unwrap();
let manifest = manifest(&task);
rfs::write(dir.path().join("Cargo.toml"), &manifest);
rfs::write(dir.path().join("lib.rs"), "#![no_std]");
let mut args = vec!["build", "--release", "-Zbuild-std=core", "--target", &task.target, "-j1"];
if task.panic == "immediate-abort" {
args.push("-Zpanic-immediate-abort");
}
cargo()
.current_dir(dir.path())
.args(&args)
.env("RUSTC_BOOTSTRAP", "1")
// Visual Studio 2022 requires that the LIB env var be set so it can
// find the Windows SDK.
.env("LIB", std::env::var("LIB").unwrap_or_default())
.context(&format!(
"build-std for target `{}` failed with the following Cargo.toml:\n\n{manifest}",
task.target
))
.run();
}