Skip to content

Commit ea82b11

Browse files
authored
Rollup merge of #118647 - onur-ozkan:bootstrap-shims-dump, r=clubby789
dump bootstrap shims When making changes to the bootstrap that shouldn't change its behavior, this feature will help developers perform comparisons to check whether the bootstrap behavior has changed or not. As an example, when removing Python from the bootstrap by migrating to Rust, this feature will be super useful for ensuring that the behavior remains unaffected. It will assist me in performing comparisons to verify that the bootstrap behavior and its outputs remains consistent throughout the migration process. This can also be used for different purposes. For example, allowing CI to dump the shims and upload them so that developers can download them and compare with their local dump to see if CI affects the bootstrap unexpectedly. Or, make CI perform comparisons on specific bootstrap tests to check for behavior changes between the master and PR branches.
2 parents 670ba47 + 2de3cf8 commit ea82b11

File tree

13 files changed

+158
-26
lines changed

13 files changed

+158
-26
lines changed

src/bootstrap/src/bin/main.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use std::io::Write;
1010
#[cfg(all(any(unix, windows), not(target_os = "solaris")))]
1111
use std::process;
1212
use std::{
13-
env, fs,
14-
io::{self, IsTerminal},
13+
env,
14+
fs::{self, OpenOptions},
15+
io::{self, BufRead, BufReader, IsTerminal},
1516
};
1617

1718
use bootstrap::{
@@ -79,6 +80,9 @@ fn main() {
7980
}
8081

8182
let pre_commit = config.src.join(".git").join("hooks").join("pre-commit");
83+
let dump_bootstrap_shims = config.dump_bootstrap_shims;
84+
let out_dir = config.out.clone();
85+
8286
Build::new(config).build();
8387

8488
if suggest_setup {
@@ -107,6 +111,29 @@ fn main() {
107111
if suggest_setup || changelog_suggestion.is_some() {
108112
println!("NOTE: this message was printed twice to make it more likely to be seen");
109113
}
114+
115+
if dump_bootstrap_shims {
116+
let dump_dir = out_dir.join("bootstrap-shims-dump");
117+
assert!(dump_dir.exists());
118+
119+
for entry in walkdir::WalkDir::new(&dump_dir) {
120+
let entry = t!(entry);
121+
122+
if !entry.file_type().is_file() {
123+
continue;
124+
}
125+
126+
let file = t!(fs::File::open(&entry.path()));
127+
128+
// To ensure deterministic results we must sort the dump lines.
129+
// This is necessary because the order of rustc invocations different
130+
// almost all the time.
131+
let mut lines: Vec<String> = t!(BufReader::new(&file).lines().collect());
132+
lines.sort_by_key(|t| t.to_lowercase());
133+
let mut file = t!(OpenOptions::new().write(true).truncate(true).open(&entry.path()));
134+
t!(file.write_all(lines.join("\n").as_bytes()));
135+
}
136+
}
110137
}
111138

112139
fn check_version(config: &Config) -> Option<String> {

src/bootstrap/src/bin/rustc.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ fn main() {
3232
let args = env::args_os().skip(1).collect::<Vec<_>>();
3333
let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
3434

35-
// We don't use the stage in this shim, but let's parse it to make sure that we're invoked
36-
// by bootstrap, or that we provide a helpful error message if not.
37-
bin_helpers::parse_rustc_stage();
35+
let stage = bin_helpers::parse_rustc_stage();
3836
let verbose = bin_helpers::parse_rustc_verbose();
3937

4038
// Detect whether or not we're a build script depending on whether --target
@@ -214,6 +212,8 @@ fn main() {
214212
}
215213
}
216214

215+
bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd);
216+
217217
let start = Instant::now();
218218
let (child, status) = {
219219
let errmsg = format!("\nFailed to run:\n{cmd:?}\n-------------");

src/bootstrap/src/bin/rustdoc.rs

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ fn main() {
6262
cmd.arg("-Zunstable-options");
6363
cmd.arg("--check-cfg=cfg(bootstrap)");
6464

65+
bin_helpers::maybe_dump(format!("stage{stage}-rustdoc"), &cmd);
66+
6567
if verbose > 1 {
6668
eprintln!(
6769
"rustdoc command: {:?}={:?} {:?}",

src/bootstrap/src/core/build_steps/clean.rs

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ fn clean_default(build: &Build) {
146146
rm_rf(&build.out.join("tmp"));
147147
rm_rf(&build.out.join("dist"));
148148
rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id"));
149+
rm_rf(&build.out.join("bootstrap-shims-dump"));
149150
rm_rf(&build.out.join("rustfmt.stamp"));
150151

151152
for host in &build.hosts {

src/bootstrap/src/core/builder.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ use crate::core::build_steps::tool::{self, SourceType};
1818
use crate::core::build_steps::{check, clean, compile, dist, doc, install, run, setup, test};
1919
use crate::core::config::flags::{Color, Subcommand};
2020
use crate::core::config::{DryRun, SplitDebuginfo, TargetSelection};
21+
use crate::prepare_behaviour_dump_dir;
2122
use crate::utils::cache::{Cache, Interned, INTERNER};
2223
use crate::utils::helpers::{self, add_dylib_path, add_link_lib_path, exe, linker_args};
2324
use crate::utils::helpers::{libdir, linker_flags, output, t, LldThreads};
24-
use crate::Crate;
2525
use crate::EXTRA_CHECK_CFGS;
26-
use crate::{Build, CLang, DocTests, GitRepo, Mode};
26+
use crate::{Build, CLang, Crate, DocTests, GitRepo, Mode};
2727

2828
pub use crate::Compiler;
2929

@@ -1788,6 +1788,16 @@ impl<'a> Builder<'a> {
17881788

17891789
// Enable usage of unstable features
17901790
cargo.env("RUSTC_BOOTSTRAP", "1");
1791+
1792+
if self.config.dump_bootstrap_shims {
1793+
prepare_behaviour_dump_dir(&self.build);
1794+
1795+
cargo
1796+
.env("DUMP_BOOTSTRAP_SHIMS", self.build.out.join("bootstrap-shims-dump"))
1797+
.env("BUILD_OUT", &self.build.out)
1798+
.env("CARGO_HOME", t!(home::cargo_home()));
1799+
};
1800+
17911801
self.add_rust_test_threads(&mut cargo);
17921802

17931803
// Almost all of the crates that we compile as part of the bootstrap may

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

+2
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ pub struct Config {
193193
pub cmd: Subcommand,
194194
pub incremental: bool,
195195
pub dry_run: DryRun,
196+
pub dump_bootstrap_shims: bool,
196197
/// Arguments appearing after `--` to be forwarded to tools,
197198
/// e.g. `--fix-broken` or test arguments.
198199
pub free_args: Vec<String>,
@@ -1210,6 +1211,7 @@ impl Config {
12101211
config.cmd = flags.cmd;
12111212
config.incremental = flags.incremental;
12121213
config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled };
1214+
config.dump_bootstrap_shims = flags.dump_bootstrap_shims;
12131215
config.keep_stage = flags.keep_stage;
12141216
config.keep_stage_std = flags.keep_stage_std;
12151217
config.color = flags.color;

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

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ pub struct Flags {
8585
#[arg(global(true), long)]
8686
/// dry run; don't build anything
8787
pub dry_run: bool,
88+
/// Indicates whether to dump the work done from bootstrap shims
89+
#[arg(global(true), long)]
90+
pub dump_bootstrap_shims: bool,
8891
#[arg(global(true), value_hint = clap::ValueHint::Other, long, value_name = "N")]
8992
/// stage to build (indicates compiler to use/test, e.g., stage 0 uses the
9093
/// bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)

src/bootstrap/src/lib.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1871,3 +1871,22 @@ pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String {
18711871

18721872
hex::encode(hasher.finalize().as_slice())
18731873
}
1874+
1875+
/// Ensures that the behavior dump directory is properly initialized.
1876+
pub fn prepare_behaviour_dump_dir(build: &Build) {
1877+
static INITIALIZED: OnceLock<bool> = OnceLock::new();
1878+
1879+
let dump_path = build.out.join("bootstrap-shims-dump");
1880+
1881+
let initialized = INITIALIZED.get().unwrap_or_else(|| &false);
1882+
if !initialized {
1883+
// clear old dumps
1884+
if dump_path.exists() {
1885+
t!(fs::remove_dir_all(&dump_path));
1886+
}
1887+
1888+
t!(fs::create_dir_all(&dump_path));
1889+
1890+
t!(INITIALIZED.set(true));
1891+
}
1892+
}

src/bootstrap/src/utils/bin_helpers.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
//! dependency on the bootstrap library. This reduces the binary size and
33
//! improves compilation time by reducing the linking time.
44
5+
use std::env;
6+
use std::fs::OpenOptions;
7+
use std::io::Write;
8+
use std::process::Command;
9+
use std::str::FromStr;
10+
511
/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
612
/// If it was not defined, returns 0 by default.
713
///
814
/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
915
pub(crate) fn parse_rustc_verbose() -> usize {
10-
use std::str::FromStr;
11-
12-
match std::env::var("RUSTC_VERBOSE") {
16+
match env::var("RUSTC_VERBOSE") {
1317
Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
1418
Err(_) => 0,
1519
}
@@ -19,10 +23,29 @@ pub(crate) fn parse_rustc_verbose() -> usize {
1923
///
2024
/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
2125
pub(crate) fn parse_rustc_stage() -> String {
22-
std::env::var("RUSTC_STAGE").unwrap_or_else(|_| {
26+
env::var("RUSTC_STAGE").unwrap_or_else(|_| {
2327
// Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
2428
eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set");
2529
eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap");
2630
std::process::exit(101);
2731
})
2832
}
33+
34+
/// Writes the command invocation to a file if `DUMP_BOOTSTRAP_SHIMS` is set during bootstrap.
35+
///
36+
/// Before writing it, replaces user-specific values to create generic dumps for cross-environment
37+
/// comparisons.
38+
pub(crate) fn maybe_dump(dump_name: String, cmd: &Command) {
39+
if let Ok(dump_dir) = env::var("DUMP_BOOTSTRAP_SHIMS") {
40+
let dump_file = format!("{dump_dir}/{dump_name}");
41+
42+
let mut file =
43+
OpenOptions::new().create(true).write(true).append(true).open(&dump_file).unwrap();
44+
45+
let cmd_dump = format!("{:?}\n", cmd);
46+
let cmd_dump = cmd_dump.replace(&env::var("BUILD_OUT").unwrap(), "${BUILD_OUT}");
47+
let cmd_dump = cmd_dump.replace(&env::var("CARGO_HOME").unwrap(), "${CARGO_HOME}");
48+
49+
file.write_all(cmd_dump.as_bytes()).expect("Unable to write file");
50+
}
51+
}

0 commit comments

Comments
 (0)