Skip to content

Commit 9eb8927

Browse files
Auto merge of #142629 - Kobzol:bootstrap-tests-builder, r=<try>
Add config builder for bootstrap tests I started writing a bunch of snapshot tests for build/check steps, and quickly realized that the current interface for defining them won't be enough, so I created a simple builder, which can scale to pretty much any kind of configuration in the future. try-job: aarch64-apple
2 parents 5b74275 + d475e10 commit 9eb8927

File tree

4 files changed

+114
-19
lines changed

4 files changed

+114
-19
lines changed

src/bootstrap/src/core/builder/tests.rs

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::Flags;
99
use crate::core::build_steps::doc::DocumentationFormat;
1010
use crate::core::config::Config;
1111
use crate::utils::cache::ExecutedStep;
12+
use crate::utils::helpers::get_host_target;
1213
use crate::utils::tests::git::{GitCtx, git_test};
1314

1415
static TEST_TRIPLE_1: &str = "i686-unknown-haiku";
@@ -1236,29 +1237,48 @@ fn any_debug() {
12361237
/// The staging tests use insta for snapshot testing.
12371238
/// See bootstrap's README on how to bless the snapshots.
12381239
mod staging {
1240+
use crate::Build;
1241+
use crate::core::builder::Builder;
12391242
use crate::core::builder::tests::{
12401243
TEST_TRIPLE_1, configure, configure_with_args, render_steps, run_build,
12411244
};
1245+
use crate::utils::tests::{ConfigBuilder, TestCtx};
12421246

12431247
#[test]
12441248
fn build_compiler_stage_1() {
1245-
let mut cache = run_build(
1246-
&["compiler".into()],
1247-
configure_with_args(&["build", "--stage", "1"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
1248-
);
1249-
let steps = cache.into_executed_steps();
1250-
insta::assert_snapshot!(render_steps(&steps), @r"
1251-
[build] rustc 0 <target1> -> std 0 <target1>
1252-
[build] llvm <target1>
1253-
[build] rustc 0 <target1> -> rustc 1 <target1>
1254-
[build] rustc 0 <target1> -> rustc 1 <target1>
1249+
let ctx = TestCtx::new();
1250+
insta::assert_snapshot!(
1251+
ctx.config("build")
1252+
.path("compiler")
1253+
.stage(1)
1254+
.get_steps(), @r"
1255+
[build] rustc 0 <host> -> std 0 <host>
1256+
[build] llvm <host>
1257+
[build] rustc 0 <host> -> rustc 1 <host>
1258+
[build] rustc 0 <host> -> rustc 1 <host>
12551259
");
12561260
}
1261+
1262+
impl ConfigBuilder {
1263+
fn get_steps(self) -> String {
1264+
let config = self.create_config();
1265+
1266+
let kind = config.cmd.kind();
1267+
let build = Build::new(config);
1268+
let builder = Builder::new(&build);
1269+
builder.run_step_descriptions(&Builder::get_step_descriptions(kind), &builder.paths);
1270+
render_steps(&builder.cache.into_executed_steps())
1271+
}
1272+
}
12571273
}
12581274

12591275
/// Renders the executed bootstrap steps for usage in snapshot tests with insta.
12601276
/// Only renders certain important steps.
12611277
/// Each value in `steps` should be a tuple of (Step, step output).
1278+
///
1279+
/// The arrow in the rendered output (`X -> Y`) means `X builds Y`.
1280+
/// This is similar to the output printed by bootstrap to stdout, but here it is
1281+
/// generated purely for the purpose of tests.
12621282
fn render_steps(steps: &[ExecutedStep]) -> String {
12631283
steps
12641284
.iter()
@@ -1275,18 +1295,17 @@ fn render_steps(steps: &[ExecutedStep]) -> String {
12751295
}
12761296
let stage =
12771297
if let Some(stage) = metadata.stage { format!("{stage} ") } else { "".to_string() };
1278-
write!(record, "{} {stage}<{}>", metadata.name, metadata.target);
1298+
write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target));
12791299
Some(record)
12801300
})
1281-
.map(|line| {
1282-
line.replace(TEST_TRIPLE_1, "target1")
1283-
.replace(TEST_TRIPLE_2, "target2")
1284-
.replace(TEST_TRIPLE_3, "target3")
1285-
})
12861301
.collect::<Vec<_>>()
12871302
.join("\n")
12881303
}
12891304

1305+
fn normalize_target(target: TargetSelection) -> String {
1306+
target.to_string().replace(&get_host_target().to_string(), "host")
1307+
}
1308+
12901309
fn render_compiler(compiler: Compiler) -> String {
1291-
format!("rustc {} <{}>", compiler.stage, compiler.host)
1310+
format!("rustc {} <{}>", compiler.stage, normalize_target(compiler.host))
12921311
}

src/bootstrap/src/core/config/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use crate::core::download::is_download_ci_available;
4949
use crate::utils::channel;
5050
use crate::utils::exec::command;
5151
use crate::utils::execution_context::ExecutionContext;
52-
use crate::utils::helpers::exe;
52+
use crate::utils::helpers::{exe, get_host_target};
5353
use crate::{GitInfo, OnceLock, TargetSelection, check_ci_llvm, helpers, t};
5454

5555
/// Each path in this list is considered "allowed" in the `download-rustc="if-unchanged"` logic.
@@ -349,7 +349,7 @@ impl Config {
349349
stderr_is_tty: std::io::stderr().is_terminal(),
350350

351351
// set by build.rs
352-
host_target: TargetSelection::from_user(env!("BUILD_TRIPLE")),
352+
host_target: get_host_target(),
353353

354354
src: {
355355
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

src/bootstrap/src/utils/helpers.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<
178178
}
179179
}
180180

181+
/// Return the host target on which we are currently running.
182+
pub fn get_host_target() -> TargetSelection {
183+
TargetSelection::from_user(env!("BUILD_TRIPLE"))
184+
}
185+
181186
/// Rename a file if from and to are in the same filesystem or
182187
/// copy and remove the file otherwise
183188
pub fn move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {

src/bootstrap/src/utils/tests/mod.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,74 @@
11
//! This module contains shared utilities for bootstrap tests.
22
3+
use std::path::{Path, PathBuf};
4+
use std::thread;
5+
6+
use tempfile::TempDir;
7+
8+
use crate::core::builder::Builder;
9+
use crate::core::config::DryRun;
10+
use crate::{Build, Config, Flags, t};
11+
312
pub mod git;
13+
14+
/// Holds temporary state of a bootstrap test.
15+
/// Right now it is only used to redirect the build directory of the bootstrap
16+
/// invocation, in the future it would be great if we could actually execute
17+
/// the whole test with this directory set as the workdir.
18+
pub struct TestCtx {
19+
directory: TempDir,
20+
}
21+
22+
impl TestCtx {
23+
pub fn new() -> Self {
24+
let directory = TempDir::new().expect("cannot create temporary directory");
25+
eprintln!("Running test in {}", directory.path().display());
26+
Self { directory }
27+
}
28+
29+
/// Starts a new invocation of bootstrap that executes `kind` as its top level command
30+
/// (i.e. `x <kind>`). Returns a builder that configures the created config through CLI flags.
31+
pub fn config(&self, kind: &str) -> ConfigBuilder {
32+
ConfigBuilder::from_args(&[kind], self.directory.path().to_owned())
33+
}
34+
}
35+
36+
/// Used to configure an invocation of bootstrap.
37+
/// Currently runs in the rustc checkout, long-term it should be switched
38+
/// to run in a (cache-primed) temporary directory instead.
39+
pub struct ConfigBuilder {
40+
args: Vec<String>,
41+
directory: PathBuf,
42+
}
43+
44+
impl ConfigBuilder {
45+
fn from_args(args: &[&str], directory: PathBuf) -> Self {
46+
Self { args: args.iter().copied().map(String::from).collect(), directory }
47+
}
48+
49+
pub fn path(mut self, path: &str) -> Self {
50+
self.args.push(path.to_string());
51+
self
52+
}
53+
54+
pub fn stage(mut self, stage: u32) -> Self {
55+
self.args.push("--stage".to_string());
56+
self.args.push(stage.to_string());
57+
self
58+
}
59+
60+
pub fn create_config(mut self) -> Config {
61+
// Run in dry-check, otherwise the test would be too slow
62+
self.args.push("--dry-run".to_string());
63+
64+
// Ignore submodules
65+
self.args.push("--set".to_string());
66+
self.args.push("build.submodules=false".to_string());
67+
68+
// Do not mess with the local rustc checkout build directory
69+
self.args.push("--build-dir".to_string());
70+
self.args.push(self.directory.join("build").display().to_string());
71+
72+
Config::parse(Flags::parse(&self.args))
73+
}
74+
}

0 commit comments

Comments
 (0)