From 1bc19932e6f0a5ce098af6f3d59e28a61abbc631 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 15 Sep 2025 19:08:55 +0530 Subject: [PATCH 01/20] make cargo test work for bootstrap self test --- src/bootstrap/src/core/config/config.rs | 24 ++++----- src/bootstrap/src/core/config/tests.rs | 62 ++---------------------- src/bootstrap/src/utils/helpers/tests.rs | 4 +- 3 files changed, 17 insertions(+), 73 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2d5a1fd5332..19fcaeaaa24de 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -630,19 +630,9 @@ impl Config { let llvm_assertions = llvm_assertions.unwrap_or(false); let mut target_config = HashMap::new(); let mut channel = "dev".to_string(); - let out = flags_build_dir.or(build_build_dir.map(PathBuf::from)).unwrap_or_else(|| { - if cfg!(test) { - // Use the build directory of the original x.py invocation, so that we can set `initial_rustc` properly. - Path::new( - &env::var_os("CARGO_TARGET_DIR").expect("cargo test directly is not supported"), - ) - .parent() - .unwrap() - .to_path_buf() - } else { - PathBuf::from("build") - } - }); + let out = flags_build_dir + .or(build_build_dir.map(PathBuf::from)) + .unwrap_or_else(|| PathBuf::from("build")); // NOTE: Bootstrap spawns various commands with different working directories. // To avoid writing to random places on the file system, `config.out` needs to be an absolute path. @@ -693,8 +683,14 @@ impl Config { }; let initial_rustc = build_rustc.unwrap_or_else(|| { + let out = if cfg!(test) { Path::new("../../build").to_path_buf() } else { out.clone() }; + download_beta_toolchain(&dwn_ctx, &out); - out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) + + out.join(if cfg!(test) { get_host_target() } else { host_target }) + .join("stage0") + .join("bin") + .join(exe("rustc", host_target)) }); let initial_sysroot = t!(PathBuf::from_str( diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index e93525fbd09ce..46d05fbc06864 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -39,7 +39,11 @@ fn prepare_test_specific_dir() -> PathBuf { // Replace "::" with "_" to make it safe for directory names on Windows systems let test_path = current.name().unwrap().replace("::", "_"); - let testdir = parse("").tempdir().join(test_path); + let testdir = crate::utils::tests::TestCtx::new() + .config("check") + .create_config() + .tempdir() + .join(test_path); // clean up any old test files let _ = fs::remove_dir_all(&testdir); @@ -64,62 +68,6 @@ fn download_ci_llvm() { } } -// FIXME(onur-ozkan): extend scope of the test -// refs: -// - https://github.com/rust-lang/rust/issues/109120 -// - https://github.com/rust-lang/rust/pull/109162#issuecomment-1496782487 -#[test] -fn detect_src_and_out() { - fn test(cfg: Config, build_dir: Option<&str>) { - // This will bring absolute form of `src/bootstrap` path - let current_dir = std::env::current_dir().unwrap(); - - // get `src` by moving into project root path - let expected_src = current_dir.ancestors().nth(2).unwrap(); - assert_eq!(&cfg.src, expected_src); - - // Sanity check for `src` - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); - let expected_src = manifest_dir.ancestors().nth(2).unwrap(); - assert_eq!(&cfg.src, expected_src); - - // test if build-dir was manually given in bootstrap.toml - if let Some(custom_build_dir) = build_dir { - assert_eq!(&cfg.out, Path::new(custom_build_dir)); - } - // test the native bootstrap way - else { - // This should bring output path of bootstrap in absolute form - let cargo_target_dir = env::var_os("CARGO_TARGET_DIR").expect( - "CARGO_TARGET_DIR must been provided for the test environment from bootstrap", - ); - - // Move to `build` from `build/bootstrap` - let expected_out = Path::new(&cargo_target_dir).parent().unwrap(); - assert_eq!(&cfg.out, expected_out); - - let args: Vec = env::args().collect(); - - // Another test for `out` as a sanity check - // - // This will bring something similar to: - // `{build-dir}/bootstrap/debug/deps/bootstrap-c7ee91d5661e2804` - // `{build-dir}` can be anywhere, not just in the rust project directory. - let dep = Path::new(args.first().unwrap()); - let expected_out = dep.ancestors().nth(5).unwrap(); - - assert_eq!(&cfg.out, expected_out); - } - } - - test(parse(""), None); - - { - let build_dir = if cfg!(windows) { "C:\\tmp" } else { "/tmp" }; - test(parse(&format!("build.build-dir = '{build_dir}'")), Some(build_dir)); - } -} - #[test] fn clap_verify() { Flags::command().debug_assert(); diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 9030ca2820a8b..e586fe77b4f62 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -6,6 +6,7 @@ use crate::utils::helpers::{ check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, symlink_dir, }; +use crate::utils::tests::TestCtx; use crate::{Config, Flags}; #[test] @@ -80,8 +81,7 @@ fn test_symlink_dir() { #[test] fn test_set_file_times_sanity_check() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + let config = TestCtx::new().config("check").create_config(); let tempfile = config.tempdir().join(".tmp-file"); { From a12969e0d10387ad17755879e98cf5097a937473 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 15 Sep 2025 19:20:05 +0530 Subject: [PATCH 02/20] walk up the ancestors --- src/bootstrap/src/core/config/config.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 19fcaeaaa24de..003cf3f2f9c3e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -683,14 +683,15 @@ impl Config { }; let initial_rustc = build_rustc.unwrap_or_else(|| { - let out = if cfg!(test) { Path::new("../../build").to_path_buf() } else { out.clone() }; - + let out = if cfg!(test) { + std::env::current_dir().unwrap().ancestors().nth(2).unwrap().join("build") + } else { + out.clone() + }; download_beta_toolchain(&dwn_ctx, &out); + let target = if cfg!(test) { get_host_target() } else { host_target }; - out.join(if cfg!(test) { get_host_target() } else { host_target }) - .join("stage0") - .join("bin") - .join(exe("rustc", host_target)) + out.join(target).join("stage0").join("bin").join(exe("rustc", host_target)) }); let initial_sysroot = t!(PathBuf::from_str( From 6c79f547f9ecf72ff8b9748dc0001d92217dff71 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:16:29 +0530 Subject: [PATCH 03/20] add an API in ConfigBuilder to point to config file for toml parsing --- src/bootstrap/src/utils/tests/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 3332187e2a853..ff84bfdb4cde3 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -98,6 +98,14 @@ impl ConfigBuilder { self } + pub fn config_toml(mut self, config_toml: &str) -> Self { + let toml_path = self.directory.join("bootstrap.toml"); + std::fs::write(&toml_path, config_toml).unwrap(); + self.args.push("--config".to_string()); + self.args.push(toml_path.display().to_string()); + self + } + pub fn create_config(mut self) -> Config { // Run in dry-check, otherwise the test would be too slow self.args.push("--dry-run".to_string()); From d488d33fd6f7d4832cab12d0dece896bf1b975c0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:17:15 +0530 Subject: [PATCH 04/20] let verify method run in test settings --- src/bootstrap/src/core/download.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 2f3c80559c0ef..ce3f49a35b9e6 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -857,7 +857,7 @@ pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) - println!("verifying {}", path.display()); }); - if exec_ctx.dry_run() { + if exec_ctx.dry_run() && !cfg!(test) { return false; } From ce4604e34e6f29740b41306806c6bd7947e103a4 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:18:35 +0530 Subject: [PATCH 05/20] remove using default toml config for test in get_toml, we handle it via using the temp directory created via the testCtx --- src/bootstrap/src/core/config/toml/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index 7af22432ef8ca..f6dc5b67e1010 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -152,10 +152,6 @@ impl Config { } pub(crate) fn get_toml(file: &Path) -> Result { - #[cfg(test)] - return Ok(TomlConfig::default()); - - #[cfg(not(test))] Self::get_toml_inner(file) } From 24ed1a0455ced62f48402f6740effbf818f45269 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:18:58 +0530 Subject: [PATCH 06/20] allow symlinking during test --- src/bootstrap/src/utils/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index e802c0214ddcb..0561f343a8c19 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -160,7 +160,7 @@ impl Drop for TimeIt { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { - if config.dry_run() { + if config.dry_run() && !cfg!(test) { return Ok(()); } let _ = fs::remove_dir_all(link); From 05131bd5f11812390018dddc29fb9a36c4657ed2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:19:29 +0530 Subject: [PATCH 07/20] move most of the test to new testCtx --- src/bootstrap/src/core/builder/tests.rs | 31 +-- src/bootstrap/src/core/config/tests.rs | 305 +++++++++++------------ src/bootstrap/src/utils/helpers/tests.rs | 7 +- 3 files changed, 152 insertions(+), 191 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 229adf714598b..a3cb4660ffc51 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -218,18 +218,16 @@ fn prepare_rustc_checkout(ctx: &mut GitCtx) { /// Parses a Config directory from `path`, with the given value of `download_rustc`. fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) -> Config { - Config::parse_inner( - Flags::parse(&[ - "build".to_owned(), - "--dry-run".to_owned(), - "--ci".to_owned(), - if ci { "true" } else { "false" }.to_owned(), - format!("--set=rust.download-rustc='{download_rustc}'"), - "--src".to_owned(), - path.to_str().unwrap().to_owned(), - ]), - |&_| Ok(Default::default()), - ) + TestCtx::new() + .config("build") + .args(&[ + "--ci", + if ci { "true" } else { "false" }, + format!("--set=rust.download-rustc='{download_rustc}'").as_str(), + "--src", + path.to_str().unwrap(), + ]) + .create_config_without_ci_llvm_override() } mod dist { @@ -359,14 +357,7 @@ fn test_test_coverage() { #[test] fn test_prebuilt_llvm_config_path_resolution() { fn configure(config: &str) -> Config { - Config::parse_inner( - Flags::parse(&[ - "build".to_string(), - "--dry-run".to_string(), - "--config=/does/not/exist".to_string(), - ]), - |&_| toml::from_str(&config), - ) + TestCtx::new().config("build").config_toml(config).create_config() } // Removes Windows disk prefix if present diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 46d05fbc06864..68859c6545e10 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -14,17 +14,15 @@ use super::toml::change_id::ChangeIdWrapper; use super::{Config, RUSTC_IF_UNCHANGED_ALLOWED_PATHS}; use crate::ChangeId; use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order}; -use crate::core::build_steps::llvm; use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS; +use crate::core::build_steps::{llvm, test}; use crate::core::config::toml::TomlConfig; use crate::core::config::{CompilerBuiltins, LldMode, StringOrBool, Target, TargetSelection}; +use crate::utils::tests::TestCtx; use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { - Config::parse_inner( - Flags::parse(&["check".to_string(), "--config=/does/not/exist".to_string()]), - |&_| toml::from_str(&config), - ) + TestCtx::new().config("check").config_toml(config).create_config() } fn get_toml(file: &Path) -> Result { @@ -54,10 +52,14 @@ fn prepare_test_specific_dir() -> PathBuf { #[test] fn download_ci_llvm() { - let config = parse("llvm.download-ci-llvm = false"); + let config = TestCtx::new().config("check").create_config(); assert!(!config.llvm_from_ci); - let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\""); + // this doesn't make sense, as we are overriding it later. + let if_unchanged_config = TestCtx::new() + .config("check") + .config_toml("llvm.download-ci-llvm = \"if-unchanged\"") + .create_config(); if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); @@ -75,54 +77,51 @@ fn clap_verify() { #[test] fn override_toml() { - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--set=change-id=1".to_owned(), - "--set=rust.lto=fat".to_owned(), - "--set=rust.deny-warnings=false".to_owned(), - "--set=build.optimized-compiler-builtins=true".to_owned(), - "--set=build.gdb=\"bar\"".to_owned(), - "--set=build.tools=[\"cargo\"]".to_owned(), - "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), - "--set=target.x86_64-unknown-linux-gnu.runner=bar".to_owned(), - "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), - "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), - "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), - "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false".to_owned(), - ]), - |&_| { - toml::from_str( - r#" -change-id = 0 -[rust] -lto = "off" -deny-warnings = true -download-rustc=false - -[build] -gdb = "foo" -tools = [] - -[llvm] -download-ci-llvm = false -build-config = {} - -[target.aarch64-unknown-linux-gnu] -sanitizers = true -rpath = true -runner = "aarch64-runner" - -[target.x86_64-unknown-linux-gnu] -sanitizers = true -rpath = true -runner = "x86_64-runner" - - "#, - ) - }, - ); + let config_toml: &str = r#" + change-id = 0 + + [rust] + lto = "off" + deny-warnings = true + download-rustc = false + + [build] + gdb = "foo" + tools = [] + + [llvm] + download-ci-llvm = false + build-config = {} + + [target.aarch64-unknown-linux-gnu] + sanitizers = true + rpath = true + runner = "aarch64-runner" + + [target.x86_64-unknown-linux-gnu] + sanitizers = true + rpath = true + runner = "x86_64-runner" + "#; + + let args = [ + "--set=change-id=1", + "--set=rust.lto=fat", + "--set=rust.deny-warnings=false", + "--set=build.optimized-compiler-builtins=true", + "--set=build.gdb=\"bar\"", + "--set=build.tools=[\"cargo\"]", + "--set=llvm.build-config={\"foo\" = \"bar\"}", + "--set=target.x86_64-unknown-linux-gnu.runner=bar", + "--set=target.x86_64-unknown-linux-gnu.rpath=false", + "--set=target.aarch64-unknown-linux-gnu.sanitizers=false", + "--set=target.aarch64-apple-darwin.runner=apple", + "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false", + ]; + + let config = + TestCtx::new().config("check").config_toml(config_toml).args(&args).create_config(); + assert_eq!(config.change_id, Some(ChangeId::Id(1)), "setting top-level value"); assert_eq!( config.rust_lto, @@ -181,15 +180,14 @@ runner = "x86_64-runner" #[test] #[should_panic] fn override_toml_duplicate() { - Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_string(), - "--set=change-id=1".to_owned(), - "--set=change-id=2".to_owned(), - ]), - |&_| toml::from_str("change-id = 0"), - ); + TestCtx::new() + .config("check") + .config_toml("change-id = 0") + .arg("--set") + .arg("change-id=1") + .arg("--set") + .arg("change-id=2") + .create_config(); } #[test] @@ -225,12 +223,12 @@ fn rust_optimize() { #[test] #[should_panic] fn invalid_rust_optimize() { - parse("rust.optimize = \"a\""); + TestCtx::new().config("check").config_toml("rust.optimize = \"a\"").create_config(); } #[test] fn verify_file_integrity() { - let config = parse(""); + let config = TestCtx::new().config("check").create_config(); let tempfile = config.tempdir().join(".tmp-test-file"); File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); @@ -272,22 +270,23 @@ fn parse_change_id_with_unknown_field() { #[test] fn order_of_clippy_rules() { - let args = vec![ - "clippy".to_string(), - "--fix".to_string(), - "--allow-dirty".to_string(), - "--allow-staged".to_string(), - "-Aclippy:all".to_string(), - "-Wclippy::style".to_string(), - "-Aclippy::foo1".to_string(), - "-Aclippy::foo2".to_string(), + let args = [ + "clippy", + "--fix", + "--allow-dirty", + "--allow-staged", + "-Aclippy:all", + "-Wclippy::style", + "-Aclippy::foo1", + "-Aclippy::foo2", ]; - let config = Config::parse(Flags::parse(&args)); + let config = TestCtx::new().config(&args[0]).args(&args[1..]).create_config(); let actual = match config.cmd.clone() { crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { let cfg = LintConfig { allow, deny, warn, forbid }; - get_clippy_rules_in_order(&args, &cfg) + let args_vec: Vec = args.iter().map(|s| s.to_string()).collect(); + get_clippy_rules_in_order(&args_vec, &cfg) } _ => panic!("invalid subcommand"), }; @@ -304,14 +303,14 @@ fn order_of_clippy_rules() { #[test] fn clippy_rule_separate_prefix() { - let args = - vec!["clippy".to_string(), "-A clippy:all".to_string(), "-W clippy::style".to_string()]; - let config = Config::parse(Flags::parse(&args)); + let args = ["clippy", "-A clippy:all", "-W clippy::style"]; + let config = TestCtx::new().config(&args[0]).args(&args[1..]).create_config(); let actual = match config.cmd.clone() { crate::Subcommand::Clippy { allow, deny, warn, forbid, .. } => { let cfg = LintConfig { allow, deny, warn, forbid }; - get_clippy_rules_in_order(&args, &cfg) + let args_vec: Vec = args.iter().map(|s| s.to_string()).collect(); + get_clippy_rules_in_order(&args_vec, &cfg) } _ => panic!("invalid subcommand"), }; @@ -331,7 +330,10 @@ fn verbose_tests_default_value() { #[test] fn parse_rust_std_features() { - let config = parse("rust.std-features = [\"panic-unwind\", \"backtrace\"]"); + let config = TestCtx::new() + .config("check") + .config_toml("rust.std-features = [\"panic-unwind\", \"backtrace\"]") + .create_config(); let expected_features: BTreeSet = ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect(); assert_eq!(config.rust_std_features, expected_features); @@ -339,7 +341,8 @@ fn parse_rust_std_features() { #[test] fn parse_rust_std_features_empty() { - let config = parse("rust.std-features = []"); + let config = + TestCtx::new().config("check").config_toml("rust.std-features = []").create_config(); let expected_features: BTreeSet = BTreeSet::new(); assert_eq!(config.rust_std_features, expected_features); } @@ -347,70 +350,58 @@ fn parse_rust_std_features_empty() { #[test] #[should_panic] fn parse_rust_std_features_invalid() { - parse("rust.std-features = \"backtrace\""); + TestCtx::new().config("check").config_toml("rust.std-features = \"backtrace\"").create_config(); } #[test] fn parse_jobs() { - assert_eq!(parse("build.jobs = 1").jobs, Some(1)); + assert_eq!( + TestCtx::new().config("check").config_toml("build.jobs = 1").create_config().jobs, + Some(1) + ); } #[test] fn jobs_precedence() { // `--jobs` should take precedence over using `--set build.jobs`. - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--jobs=67890".to_owned(), - "--set=build.jobs=12345".to_owned(), - ]), - |&_| toml::from_str(""), - ); + let config = TestCtx::new() + .config("check") + .args(&["--jobs=67890", "--set=build.jobs=12345"]) + .create_config(); assert_eq!(config.jobs, Some(67890)); // `--set build.jobs` should take precedence over `bootstrap.toml`. - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--config=/does/not/exist".to_owned(), - "--set=build.jobs=12345".to_owned(), - ]), - |&_| { - toml::from_str( - r#" - [build] - jobs = 67890 - "#, - ) - }, - ); + let config = TestCtx::new() + .config("check") + .args(&["--set=build.jobs=12345"]) + .config_toml( + r#" + [build] + jobs = 67890 + "#, + ) + .create_config(); + assert_eq!(config.jobs, Some(12345)); // `--jobs` > `--set build.jobs` > `bootstrap.toml` - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--jobs=123".to_owned(), - "--config=/does/not/exist".to_owned(), - "--set=build.jobs=456".to_owned(), - ]), - |&_| { - toml::from_str( - r#" - [build] - jobs = 789 - "#, - ) - }, - ); + let config = TestCtx::new() + .config("check") + .args(&["--jobs=123", "--set=build.jobs=456"]) + .config_toml( + r#" + [build] + jobs = 789 + "#, + ) + .create_config(); assert_eq!(config.jobs, Some(123)); } #[test] fn check_rustc_if_unchanged_paths() { - let config = parse(""); + let config = TestCtx::new().config("check").create_config(); let normalised_allowed_paths: Vec<_> = RUSTC_IF_UNCHANGED_ALLOWED_PATHS .iter() .map(|t| { @@ -425,59 +416,42 @@ fn check_rustc_if_unchanged_paths() { #[test] fn test_explicit_stage() { - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]), - |&_| { - toml::from_str( - r#" + let config = TestCtx::new() + .config("check") + .config_toml( + r#" [build] test-stage = 1 "#, - ) - }, - ); + ) + .create_config(); assert!(!config.explicit_stage_from_cli); assert!(config.explicit_stage_from_config); assert!(config.is_explicit_stage()); - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--stage=2".to_owned(), - "--config=/does/not/exist".to_owned(), - ]), - |&_| toml::from_str(""), - ); + let config = TestCtx::new().config("check").stage(2).create_config(); assert!(config.explicit_stage_from_cli); assert!(!config.explicit_stage_from_config); assert!(config.is_explicit_stage()); - let config = Config::parse_inner( - Flags::parse(&[ - "check".to_owned(), - "--stage=2".to_owned(), - "--config=/does/not/exist".to_owned(), - ]), - |&_| { - toml::from_str( - r#" + let config = TestCtx::new() + .config("check") + .stage(2) + .config_toml( + r#" [build] test-stage = 1 "#, - ) - }, - ); + ) + .create_config(); assert!(config.explicit_stage_from_cli); assert!(config.explicit_stage_from_config); assert!(config.is_explicit_stage()); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()]), - |&_| toml::from_str(""), - ); + let config = TestCtx::new().config("check").create_config(); assert!(!config.explicit_stage_from_cli); assert!(!config.explicit_stage_from_config); @@ -487,7 +461,10 @@ fn test_explicit_stage() { #[test] fn test_exclude() { let exclude_path = "compiler"; - let config = parse(&format!("build.exclude=[\"{}\"]", exclude_path)); + let config = TestCtx::new() + .config("check") + .config_toml(&format!("build.exclude=[\"{}\"]", exclude_path)) + .create_config(); let first_excluded = config .skip @@ -501,17 +478,13 @@ fn test_exclude() { #[test] fn test_ci_flag() { - let config = Config::parse_inner(Flags::parse(&["check".into(), "--ci=false".into()]), |&_| { - toml::from_str("") - }); + let config = TestCtx::new().config("check").arg("--ci").arg("false").create_config(); assert!(!config.is_running_on_ci); - let config = Config::parse_inner(Flags::parse(&["check".into(), "--ci=true".into()]), |&_| { - toml::from_str("") - }); + let config = TestCtx::new().config("check").arg("--ci").arg("true").create_config(); assert!(config.is_running_on_ci); - let config = Config::parse_inner(Flags::parse(&["check".into()]), |&_| toml::from_str("")); + let config = TestCtx::new().config("check").create_config(); assert_eq!(config.is_running_on_ci, CiEnv::is_ci()); } diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index e586fe77b4f62..ec99743d1ed88 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -60,8 +60,7 @@ fn test_check_cfg_arg() { #[test] fn test_symlink_dir() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + let config = TestCtx::new().config("check").create_config(); let tempdir = config.tempdir().join(".tmp-dir"); let link_path = config.tempdir().join(".tmp-link"); @@ -102,9 +101,7 @@ fn test_set_file_times_sanity_check() { #[test] fn test_submodule_path_of() { - let config = Config::parse_inner(Flags::parse(&["build".into(), "--dry-run".into()]), |&_| { - Ok(Default::default()) - }); + let config = TestCtx::new().config("build").create_config(); let build = crate::Build::new(config.clone()); let builder = crate::core::builder::Builder::new(&build); From a29474d3ff06660071ebb5f071a7cdce0ee07646 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 16 Sep 2025 21:20:14 +0530 Subject: [PATCH 08/20] this is dicy, whether we have a method to explicitly enable_llvm_override --- src/bootstrap/src/utils/tests/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index ff84bfdb4cde3..d47a224d7af51 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -125,4 +125,19 @@ impl ConfigBuilder { Config::parse(Flags::parse(&self.args)) } + + pub fn create_config_without_ci_llvm_override(mut self) -> Config { + // Run in dry-check, otherwise the test would be too slow + self.args.push("--dry-run".to_string()); + + // Ignore submodules + self.args.push("--set".to_string()); + self.args.push("build.submodules=false".to_string()); + + // Do not mess with the local rustc checkout build directory + self.args.push("--build-dir".to_string()); + self.args.push(self.directory.join("build").display().to_string()); + + Config::parse(Flags::parse(&self.args)) + } } From 0c68c82957e86c15d4a543c0d32f9e101ce74ba9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 18:16:47 +0530 Subject: [PATCH 09/20] rename config_toml to with_default_toml_config --- src/bootstrap/src/core/builder/tests.rs | 2 +- src/bootstrap/src/core/config/tests.rs | 47 ++++++++++++++++--------- src/bootstrap/src/utils/tests/mod.rs | 2 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index a3cb4660ffc51..7033098b79bff 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -357,7 +357,7 @@ fn test_test_coverage() { #[test] fn test_prebuilt_llvm_config_path_resolution() { fn configure(config: &str) -> Config { - TestCtx::new().config("build").config_toml(config).create_config() + TestCtx::new().config("build").with_default_toml_config(config).create_config() } // Removes Windows disk prefix if present diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 68859c6545e10..30b4c6cfc4317 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -22,7 +22,7 @@ use crate::utils::tests::TestCtx; use crate::utils::tests::git::git_test; pub(crate) fn parse(config: &str) -> Config { - TestCtx::new().config("check").config_toml(config).create_config() + TestCtx::new().config("check").with_default_toml_config(config).create_config() } fn get_toml(file: &Path) -> Result { @@ -58,7 +58,7 @@ fn download_ci_llvm() { // this doesn't make sense, as we are overriding it later. let if_unchanged_config = TestCtx::new() .config("check") - .config_toml("llvm.download-ci-llvm = \"if-unchanged\"") + .with_default_toml_config("llvm.download-ci-llvm = \"if-unchanged\"") .create_config(); if if_unchanged_config.llvm_from_ci && if_unchanged_config.is_running_on_ci { let has_changes = if_unchanged_config.has_changes_from_upstream(LLVM_INVALIDATION_PATHS); @@ -119,8 +119,11 @@ fn override_toml() { "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false", ]; - let config = - TestCtx::new().config("check").config_toml(config_toml).args(&args).create_config(); + let config = TestCtx::new() + .config("check") + .with_default_toml_config(config_toml) + .args(&args) + .create_config(); assert_eq!(config.change_id, Some(ChangeId::Id(1)), "setting top-level value"); assert_eq!( @@ -182,7 +185,7 @@ fn override_toml() { fn override_toml_duplicate() { TestCtx::new() .config("check") - .config_toml("change-id = 0") + .with_default_toml_config("change-id = 0") .arg("--set") .arg("change-id=1") .arg("--set") @@ -223,7 +226,10 @@ fn rust_optimize() { #[test] #[should_panic] fn invalid_rust_optimize() { - TestCtx::new().config("check").config_toml("rust.optimize = \"a\"").create_config(); + TestCtx::new() + .config("check") + .with_default_toml_config("rust.optimize = \"a\"") + .create_config(); } #[test] @@ -332,7 +338,7 @@ fn verbose_tests_default_value() { fn parse_rust_std_features() { let config = TestCtx::new() .config("check") - .config_toml("rust.std-features = [\"panic-unwind\", \"backtrace\"]") + .with_default_toml_config("rust.std-features = [\"panic-unwind\", \"backtrace\"]") .create_config(); let expected_features: BTreeSet = ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect(); @@ -341,8 +347,10 @@ fn parse_rust_std_features() { #[test] fn parse_rust_std_features_empty() { - let config = - TestCtx::new().config("check").config_toml("rust.std-features = []").create_config(); + let config = TestCtx::new() + .config("check") + .with_default_toml_config("rust.std-features = []") + .create_config(); let expected_features: BTreeSet = BTreeSet::new(); assert_eq!(config.rust_std_features, expected_features); } @@ -350,13 +358,20 @@ fn parse_rust_std_features_empty() { #[test] #[should_panic] fn parse_rust_std_features_invalid() { - TestCtx::new().config("check").config_toml("rust.std-features = \"backtrace\"").create_config(); + TestCtx::new() + .config("check") + .with_default_toml_config("rust.std-features = \"backtrace\"") + .create_config(); } #[test] fn parse_jobs() { assert_eq!( - TestCtx::new().config("check").config_toml("build.jobs = 1").create_config().jobs, + TestCtx::new() + .config("check") + .with_default_toml_config("build.jobs = 1") + .create_config() + .jobs, Some(1) ); } @@ -375,7 +390,7 @@ fn jobs_precedence() { let config = TestCtx::new() .config("check") .args(&["--set=build.jobs=12345"]) - .config_toml( + .with_default_toml_config( r#" [build] jobs = 67890 @@ -389,7 +404,7 @@ fn jobs_precedence() { let config = TestCtx::new() .config("check") .args(&["--jobs=123", "--set=build.jobs=456"]) - .config_toml( + .with_default_toml_config( r#" [build] jobs = 789 @@ -418,7 +433,7 @@ fn check_rustc_if_unchanged_paths() { fn test_explicit_stage() { let config = TestCtx::new() .config("check") - .config_toml( + .with_default_toml_config( r#" [build] test-stage = 1 @@ -439,7 +454,7 @@ fn test_explicit_stage() { let config = TestCtx::new() .config("check") .stage(2) - .config_toml( + .with_default_toml_config( r#" [build] test-stage = 1 @@ -463,7 +478,7 @@ fn test_exclude() { let exclude_path = "compiler"; let config = TestCtx::new() .config("check") - .config_toml(&format!("build.exclude=[\"{}\"]", exclude_path)) + .with_default_toml_config(&format!("build.exclude=[\"{}\"]", exclude_path)) .create_config(); let first_excluded = config diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index d47a224d7af51..80df15939b057 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -98,7 +98,7 @@ impl ConfigBuilder { self } - pub fn config_toml(mut self, config_toml: &str) -> Self { + pub fn with_default_toml_config(mut self, config_toml: &str) -> Self { let toml_path = self.directory.join("bootstrap.toml"); std::fs::write(&toml_path, config_toml).unwrap(); self.args.push("--config".to_string()); From 9189bf79d43dd162858bc58d64c6c99da7ca0add Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 18:32:20 +0530 Subject: [PATCH 10/20] remove create_config_without_ci_llvm_override duplication --- src/bootstrap/src/core/builder/tests.rs | 3 +- src/bootstrap/src/utils/tests/mod.rs | 37 ++++++++++++------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 7033098b79bff..0bb22eccd1936 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -227,7 +227,8 @@ fn parse_config_download_rustc_at(path: &Path, download_rustc: &str, ci: bool) - "--src", path.to_str().unwrap(), ]) - .create_config_without_ci_llvm_override() + .no_override_download_ci_llvm() + .create_config() } mod dist { diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 80df15939b057..0ccc8e1ebb8f4 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -48,11 +48,16 @@ impl TestCtx { pub struct ConfigBuilder { args: Vec, directory: PathBuf, + override_download_ci_llvm: bool, } impl ConfigBuilder { fn from_args(args: &[&str], directory: PathBuf) -> Self { - Self { args: args.iter().copied().map(String::from).collect(), directory } + Self { + args: args.iter().copied().map(String::from).collect(), + directory, + override_download_ci_llvm: true, + } } pub fn path(mut self, path: &str) -> Self { @@ -106,27 +111,12 @@ impl ConfigBuilder { self } - pub fn create_config(mut self) -> Config { - // Run in dry-check, otherwise the test would be too slow - self.args.push("--dry-run".to_string()); - - // Ignore submodules - self.args.push("--set".to_string()); - self.args.push("build.submodules=false".to_string()); - - // Override any external LLVM set and inhibit CI LLVM; pretend that we're always building - // in-tree LLVM from sources. - self.args.push("--set".to_string()); - self.args.push("llvm.download-ci-llvm=false".to_string()); - - // Do not mess with the local rustc checkout build directory - self.args.push("--build-dir".to_string()); - self.args.push(self.directory.join("build").display().to_string()); - - Config::parse(Flags::parse(&self.args)) + pub fn no_override_download_ci_llvm(mut self) -> Self { + self.override_download_ci_llvm = false; + self } - pub fn create_config_without_ci_llvm_override(mut self) -> Config { + pub fn create_config(mut self) -> Config { // Run in dry-check, otherwise the test would be too slow self.args.push("--dry-run".to_string()); @@ -134,6 +124,13 @@ impl ConfigBuilder { self.args.push("--set".to_string()); self.args.push("build.submodules=false".to_string()); + if self.override_download_ci_llvm { + // Override any external LLVM set and inhibit CI LLVM; pretend that we're always building + // in-tree LLVM from sources. + self.args.push("--set".to_string()); + self.args.push("llvm.download-ci-llvm=false".to_string()); + } + // Do not mess with the local rustc checkout build directory self.args.push("--build-dir".to_string()); self.args.push(self.directory.join("build").display().to_string()); From 671aabd4eb1a56a09b989abc021bee32750ee59b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 19:34:49 +0530 Subject: [PATCH 11/20] add dry_run flag in config builder and remove runtime test hacks --- src/bootstrap/src/core/config/tests.rs | 2 +- src/bootstrap/src/core/download.rs | 2 +- src/bootstrap/src/utils/helpers.rs | 2 +- src/bootstrap/src/utils/helpers/tests.rs | 2 +- src/bootstrap/src/utils/tests/mod.rs | 14 +++++++++++--- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 30b4c6cfc4317..a7429e07d858c 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -234,7 +234,7 @@ fn invalid_rust_optimize() { #[test] fn verify_file_integrity() { - let config = TestCtx::new().config("check").create_config(); + let config = TestCtx::new().config("check").no_dry_run().create_config(); let tempfile = config.tempdir().join(".tmp-test-file"); File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index ce3f49a35b9e6..2f3c80559c0ef 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -857,7 +857,7 @@ pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) - println!("verifying {}", path.display()); }); - if exec_ctx.dry_run() && !cfg!(test) { + if exec_ctx.dry_run() { return false; } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 0561f343a8c19..e802c0214ddcb 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -160,7 +160,7 @@ impl Drop for TimeIt { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { - if config.dry_run() && !cfg!(test) { + if config.dry_run() { return Ok(()); } let _ = fs::remove_dir_all(link); diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index ec99743d1ed88..676fe6cbd5fe5 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -60,7 +60,7 @@ fn test_check_cfg_arg() { #[test] fn test_symlink_dir() { - let config = TestCtx::new().config("check").create_config(); + let config = TestCtx::new().config("check").no_dry_run().create_config(); let tempdir = config.tempdir().join(".tmp-dir"); let link_path = config.tempdir().join(".tmp-link"); diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index 0ccc8e1ebb8f4..dc905969dcac1 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -49,6 +49,7 @@ pub struct ConfigBuilder { args: Vec, directory: PathBuf, override_download_ci_llvm: bool, + dry_run: bool, } impl ConfigBuilder { @@ -57,6 +58,7 @@ impl ConfigBuilder { args: args.iter().copied().map(String::from).collect(), directory, override_download_ci_llvm: true, + dry_run: true, } } @@ -116,10 +118,16 @@ impl ConfigBuilder { self } - pub fn create_config(mut self) -> Config { - // Run in dry-check, otherwise the test would be too slow - self.args.push("--dry-run".to_string()); + pub fn no_dry_run(mut self) -> Self { + self.dry_run = false; + self + } + pub fn create_config(mut self) -> Config { + if self.dry_run { + // Run in dry-check, otherwise the test would be too slow + self.args.push("--dry-run".to_string()); + } // Ignore submodules self.args.push("--set".to_string()); self.args.push("build.submodules=false".to_string()); From afe380dd58170b55b100a659f05017b1149d0ec9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 19:43:43 +0530 Subject: [PATCH 12/20] initialize out with CARGO_TARGET_DIR and then go for manifest and then for current --- src/bootstrap/src/core/config/config.rs | 27 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 003cf3f2f9c3e..1f3b90d324fb1 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -630,9 +630,13 @@ impl Config { let llvm_assertions = llvm_assertions.unwrap_or(false); let mut target_config = HashMap::new(); let mut channel = "dev".to_string(); - let out = flags_build_dir - .or(build_build_dir.map(PathBuf::from)) - .unwrap_or_else(|| PathBuf::from("build")); + let out = if cfg!(test) { + test_build_dir() + } else { + flags_build_dir + .or_else(|| build_build_dir.map(PathBuf::from)) + .unwrap_or_else(|| PathBuf::from("build")) + }; // NOTE: Bootstrap spawns various commands with different working directories. // To avoid writing to random places on the file system, `config.out` needs to be an absolute path. @@ -683,11 +687,6 @@ impl Config { }; let initial_rustc = build_rustc.unwrap_or_else(|| { - let out = if cfg!(test) { - std::env::current_dir().unwrap().ancestors().nth(2).unwrap().join("build") - } else { - out.clone() - }; download_beta_toolchain(&dwn_ctx, &out); let target = if cfg!(test) { get_host_target() } else { host_target }; @@ -2481,3 +2480,15 @@ fn find_correct_section_for_field(field_name: &str) -> Vec { }) .collect() } + +fn test_build_dir() -> PathBuf { + env::var_os("CARGO_TARGET_DIR") + .map(|value| Path::new(&value).parent().unwrap().to_path_buf()) + .unwrap_or_else(|| { + let base = option_env!("CARGO_MANIFEST_DIR") + .map(PathBuf::from) + .unwrap_or_else(|| std::env::current_dir().expect("failed to get current dir")); + + base.ancestors().nth(2).unwrap_or_else(|| Path::new(".")).join("build") + }) +} From a48cd767f634ec7461b8367e5d301a2985cfce7d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 20:03:15 +0530 Subject: [PATCH 13/20] add explicit config assignment when running the test, as the src is assigned to CARGO_MANIFEST_DIR, so the config actually use the /bootstrap.toml and the /tmp/bootstrap.toml --- src/bootstrap/src/utils/tests/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs index dc905969dcac1..764b89086cf2f 100644 --- a/src/bootstrap/src/utils/tests/mod.rs +++ b/src/bootstrap/src/utils/tests/mod.rs @@ -50,6 +50,7 @@ pub struct ConfigBuilder { directory: PathBuf, override_download_ci_llvm: bool, dry_run: bool, + explicit_config: bool, } impl ConfigBuilder { @@ -59,6 +60,7 @@ impl ConfigBuilder { directory, override_download_ci_llvm: true, dry_run: true, + explicit_config: true, } } @@ -108,6 +110,7 @@ impl ConfigBuilder { pub fn with_default_toml_config(mut self, config_toml: &str) -> Self { let toml_path = self.directory.join("bootstrap.toml"); std::fs::write(&toml_path, config_toml).unwrap(); + self.explicit_config = false; self.args.push("--config".to_string()); self.args.push(toml_path.display().to_string()); self @@ -139,6 +142,12 @@ impl ConfigBuilder { self.args.push("llvm.download-ci-llvm=false".to_string()); } + // always use the bootstrap toml created in the + // temporary directory and not from the + if self.explicit_config { + self = self.with_default_toml_config(""); + } + // Do not mess with the local rustc checkout build directory self.args.push("--build-dir".to_string()); self.args.push(self.directory.join("build").display().to_string()); From 33e262e8cc5ec07e0d3312be2d2c2ae3404f5fbd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 21 Sep 2025 20:52:26 +0530 Subject: [PATCH 14/20] remove prepare_test_specific_dir and update tests accordingly --- src/bootstrap/src/core/config/tests.rs | 154 +++++++++++-------------- 1 file changed, 65 insertions(+), 89 deletions(-) diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index a7429e07d858c..f277e3704f277 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -30,26 +30,6 @@ fn get_toml(file: &Path) -> Result { toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) } -/// Helps with debugging by using consistent test-specific directories instead of -/// random temporary directories. -fn prepare_test_specific_dir() -> PathBuf { - let current = std::thread::current(); - // Replace "::" with "_" to make it safe for directory names on Windows systems - let test_path = current.name().unwrap().replace("::", "_"); - - let testdir = crate::utils::tests::TestCtx::new() - .config("check") - .create_config() - .tempdir() - .join(test_path); - - // clean up any old test files - let _ = fs::remove_dir_all(&testdir); - let _ = fs::create_dir_all(&testdir); - - testdir -} - #[test] fn download_ci_llvm() { let config = TestCtx::new().config("check").create_config(); @@ -505,16 +485,8 @@ fn test_ci_flag() { #[test] fn test_precedence_of_includes() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./extension.toml"] - - [llvm] - link-jobs = 2 - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" @@ -535,10 +507,17 @@ fn test_precedence_of_includes() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + let config = test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./extension.toml"] + + [llvm] + link-jobs = 2 + "#, + ) + .create_config(); assert_eq!(config.change_id.unwrap(), ChangeId::Id(543)); assert_eq!(config.llvm_link_jobs.unwrap(), 2); @@ -548,36 +527,29 @@ fn test_precedence_of_includes() { #[test] #[should_panic(expected = "Cyclic inclusion detected")] fn test_cyclic_include_direct() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); - + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" - include = ["./config.toml"] + include = ["./bootstrap.toml"] "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./extension.toml"] + "#, + ) + .create_config(); } #[test] #[should_panic(expected = "Cyclic inclusion detected")] fn test_cyclic_include_indirect() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" @@ -597,43 +569,37 @@ fn test_cyclic_include_indirect() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./extension.toml"] + "#, + ) + .create_config(); } #[test] fn test_include_absolute_paths() { - let testdir = prepare_test_specific_dir(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); File::create(&extension).unwrap().write_all(&[]).unwrap(); - let root_config = testdir.join("config.toml"); let extension_absolute_path = extension.canonicalize().unwrap().to_str().unwrap().replace('\\', r"\\"); let root_config_content = format!(r#"include = ["{}"]"#, extension_absolute_path); - File::create(&root_config).unwrap().write_all(root_config_content.as_bytes()).unwrap(); - - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx.config("check").with_default_toml_config(&root_config_content).create_config(); } #[test] fn test_include_relative_paths() { - let testdir = prepare_test_specific_dir(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let _ = fs::create_dir_all(&testdir.join("subdir/another_subdir")); - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - include = ["./subdir/extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); - let extension = testdir.join("subdir/extension.toml"); let extension_content = br#" include = ["../extension2.toml"] @@ -655,22 +621,20 @@ fn test_include_relative_paths() { let extension = testdir.join("extension4.toml"); File::create(extension).unwrap().write_all(&[]).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + test_ctx + .config("check") + .with_default_toml_config( + r#" + include = ["./subdir/extension.toml"] + "#, + ) + .create_config(); } #[test] fn test_include_precedence_over_profile() { - let testdir = prepare_test_specific_dir(); - - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - profile = "dist" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + let test_ctx = TestCtx::new(); + let testdir = test_ctx.dir(); let extension = testdir.join("extension.toml"); let extension_content = br#" @@ -679,10 +643,22 @@ fn test_include_precedence_over_profile() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let config = Config::parse_inner( - Flags::parse(&["check".to_owned(), format!("--config={}", root_config.to_str().unwrap())]), - get_toml, - ); + let root_config = testdir.join("config.toml"); + let root_config_content = br#" + profile = "dist" + include = ["./extension.toml"] + "#; + File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); + + let config = test_ctx + .config("check") + .with_default_toml_config( + r#" + profile = "dist" + include = ["./extension.toml"] + "#, + ) + .create_config(); // "dist" profile would normally set the channel to "auto-detect", but includes should // override profile settings, so we expect this to be "dev" here. From 6adbb3a18972c1e4c24ac8a2766a0a5fc699f900 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 18:06:07 +0530 Subject: [PATCH 15/20] remove explicit target assignment in config during rustc initialization --- src/bootstrap/src/core/builder/tests.rs | 19 +++++++------------ src/bootstrap/src/core/config/config.rs | 4 +--- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 0bb22eccd1936..f838149977d01 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -22,13 +22,7 @@ fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { } fn configure_with_args(cmd: &[&str], host: &[&str], target: &[&str]) -> Config { - TestCtx::new() - .config(cmd[0]) - .args(&cmd[1..]) - .hosts(host) - .targets(target) - .args(&["--build", TEST_TRIPLE_1]) - .create_config() + TestCtx::new().config(cmd[0]).args(&cmd[1..]).hosts(host).targets(target).create_config() } fn first(v: Vec<(A, B)>) -> Vec { @@ -236,6 +230,7 @@ mod dist { use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, first, run_build}; use crate::Flags; + use crate::core::builder::tests::host_target; use crate::core::builder::*; fn configure(host: &[&str], target: &[&str]) -> Config { @@ -244,11 +239,11 @@ mod dist { #[test] fn llvm_out_behaviour() { - let mut config = configure(&[TEST_TRIPLE_1], &[TEST_TRIPLE_2]); + let mut config = configure(&[], &[TEST_TRIPLE_2]); config.llvm_from_ci = true; let build = Build::new(config.clone()); - let target = TargetSelection::from_user(TEST_TRIPLE_1); + let target = TargetSelection::from_user(&host_target()); assert!(build.llvm_out(target).ends_with("ci-llvm")); let target = TargetSelection::from_user(TEST_TRIPLE_2); assert!(build.llvm_out(target).ends_with("llvm")); @@ -313,14 +308,14 @@ mod sysroot_target_dirs { /// cg_gcc tests instead. #[test] fn test_test_compiler() { - let config = configure_with_args(&["test", "compiler"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let config = configure_with_args(&["test", "compiler"], &[], &[TEST_TRIPLE_1]); let cache = run_build(&config.paths.clone(), config); let compiler = cache.contains::(); let cranelift = cache.contains::(); let gcc = cache.contains::(); - assert_eq!((compiler, cranelift, gcc), (true, false, false)); + assert_eq!((compiler, cranelift, gcc), (false, false, false)); } #[test] @@ -346,7 +341,7 @@ fn test_test_coverage() { // Print each test case so that if one fails, the most recently printed // case is the one that failed. println!("Testing case: {cmd:?}"); - let config = configure_with_args(cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let config = configure_with_args(cmd, &[], &[TEST_TRIPLE_1]); let mut cache = run_build(&config.paths.clone(), config); let modes = diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1f3b90d324fb1..957de5367ef6a 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -688,9 +688,7 @@ impl Config { let initial_rustc = build_rustc.unwrap_or_else(|| { download_beta_toolchain(&dwn_ctx, &out); - let target = if cfg!(test) { get_host_target() } else { host_target }; - - out.join(target).join("stage0").join("bin").join(exe("rustc", host_target)) + out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) }); let initial_sysroot = t!(PathBuf::from_str( From 8a0e3808c04c16732c4651884c2a28063f4eec43 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 18:46:18 +0530 Subject: [PATCH 16/20] add check for toml file --- src/bootstrap/src/core/config/tests.rs | 13 +++---------- src/bootstrap/src/core/config/toml/mod.rs | 6 ++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index f277e3704f277..3390e9586a4c7 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -307,10 +307,11 @@ fn clippy_rule_separate_prefix() { #[test] fn verbose_tests_default_value() { - let config = Config::parse(Flags::parse(&["build".into(), "compiler".into()])); + let config = TestCtx::new().config("build").args(&["compiler".into()]).create_config(); assert_eq!(config.verbose_tests, false); - let config = Config::parse(Flags::parse(&["build".into(), "compiler".into(), "-v".into()])); + let config = + TestCtx::new().config("build").args(&["compiler".into(), "-v".into()]).create_config(); assert_eq!(config.verbose_tests, true); } @@ -643,18 +644,10 @@ fn test_include_precedence_over_profile() { "#; File::create(extension).unwrap().write_all(extension_content).unwrap(); - let root_config = testdir.join("config.toml"); - let root_config_content = br#" - profile = "dist" - include = ["./extension.toml"] - "#; - File::create(&root_config).unwrap().write_all(root_config_content).unwrap(); - let config = test_ctx .config("check") .with_default_toml_config( r#" - profile = "dist" include = ["./extension.toml"] "#, ) diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index f6dc5b67e1010..5f86d2e728184 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -152,6 +152,12 @@ impl Config { } pub(crate) fn get_toml(file: &Path) -> Result { + #[cfg(test)] + { + let tmp = std::env::temp_dir(); + assert!(file.starts_with(&tmp), "Expected path in temp dir {:?}, got {:?}", tmp, file); + } + Self::get_toml_inner(file) } From 83b784fda12a3422bfa7ce14c1bbd9864df70e5e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 18:54:23 +0530 Subject: [PATCH 17/20] add comment explaining the test_build_dir --- src/bootstrap/src/core/config/config.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 957de5367ef6a..280ae088f3f3d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2479,6 +2479,13 @@ fn find_correct_section_for_field(field_name: &str) -> Vec { .collect() } +/// Resolve the build directory used for tests. +/// +/// - When tests are run through bootstrap (`x.py test`), the build system +/// sets `CARGO_TARGET_DIR`, so we can trust and use it here. +/// - When tests are run directly with cargo test, `CARGO_TARGET_DIR` will +/// not be set. In that case we fall back to resolving relative to +/// `CARGO_MANIFEST_DIR`, by walking two parents up and appending `build`. fn test_build_dir() -> PathBuf { env::var_os("CARGO_TARGET_DIR") .map(|value| Path::new(&value).parent().unwrap().to_path_buf()) From aaa82aea053bfbb226916b38c66243a28a296b6e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 22 Sep 2025 21:52:48 +0530 Subject: [PATCH 18/20] move config check logic from get_toml to parse_inner --- src/bootstrap/src/core/builder/tests.rs | 4 ++-- src/bootstrap/src/core/config/config.rs | 12 +++++++++++ src/bootstrap/src/core/config/tests.rs | 25 ++++++++--------------- src/bootstrap/src/core/config/toml/mod.rs | 6 ------ 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index f838149977d01..4555f0d20912e 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -308,14 +308,14 @@ mod sysroot_target_dirs { /// cg_gcc tests instead. #[test] fn test_test_compiler() { - let config = configure_with_args(&["test", "compiler"], &[], &[TEST_TRIPLE_1]); + let config = configure_with_args(&["test", "compiler"], &[&host_target()], &[TEST_TRIPLE_1]); let cache = run_build(&config.paths.clone(), config); let compiler = cache.contains::(); let cranelift = cache.contains::(); let gcc = cache.contains::(); - assert_eq!((compiler, cranelift, gcc), (false, false, false)); + assert_eq!((compiler, cranelift, gcc), (true, false, false)); } #[test] diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 280ae088f3f3d..1e207380a0a1b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -424,6 +424,18 @@ impl Config { src = src_; } + #[cfg(test)] + { + if let Some(config_path) = flags_config.as_ref() { + assert!( + !config_path.starts_with(&src), + "Path {config_path:?} should not be inside or equal to src dir {src:?}" + ); + } else { + panic!("During test the config should be explicitly added"); + } + } + // Now load the TOML config, as soon as possible let (mut toml, toml_path) = load_toml_config(&src, flags_config, &get_toml); diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 3390e9586a4c7..4f2df76a15658 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -175,20 +175,14 @@ fn override_toml_duplicate() { #[test] fn profile_user_dist() { - fn get_toml(file: &Path) -> Result { - let contents = if file.ends_with("bootstrap.toml") - || file.ends_with("config.toml") - || env::var_os("RUST_BOOTSTRAP_CONFIG").is_some() - { - "profile = \"user\"".to_owned() - } else { - assert!(file.ends_with("config.dist.toml") || file.ends_with("bootstrap.dist.toml")); - std::fs::read_to_string(file).unwrap() - }; - - toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) - } - Config::parse_inner(Flags::parse(&["check".to_owned()]), get_toml); + TestCtx::new() + .config("check") + .with_default_toml_config( + r#" + profile = "user" + "#, + ) + .create_config(); } #[test] @@ -224,8 +218,6 @@ fn verify_file_integrity() { config .verify(&tempfile, "7e255dd9542648a8779268a0f268b891a198e9828e860ed23f826440e786eae5") ); - - remove_file(tempfile).unwrap(); } #[test] @@ -648,6 +640,7 @@ fn test_include_precedence_over_profile() { .config("check") .with_default_toml_config( r#" + profile = "dist" include = ["./extension.toml"] "#, ) diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index 5f86d2e728184..f6dc5b67e1010 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -152,12 +152,6 @@ impl Config { } pub(crate) fn get_toml(file: &Path) -> Result { - #[cfg(test)] - { - let tmp = std::env::temp_dir(); - assert!(file.starts_with(&tmp), "Expected path in temp dir {:?}, got {:?}", tmp, file); - } - Self::get_toml_inner(file) } From 079addf985d1902e8f79620697834c3ea3de84c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 15:07:34 +0200 Subject: [PATCH 19/20] Ensure that `--build-dir` is always specified in tests --- src/bootstrap/src/core/config/config.rs | 27 ++++--------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1e207380a0a1b..74a272c740bd3 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -642,12 +642,12 @@ impl Config { let llvm_assertions = llvm_assertions.unwrap_or(false); let mut target_config = HashMap::new(); let mut channel = "dev".to_string(); + + let out = flags_build_dir.or_else(|| build_build_dir.map(PathBuf::from)); let out = if cfg!(test) { - test_build_dir() + out.expect("--build-dir has to be specified in tests") } else { - flags_build_dir - .or_else(|| build_build_dir.map(PathBuf::from)) - .unwrap_or_else(|| PathBuf::from("build")) + out.unwrap_or_else(|| PathBuf::from("build")) }; // NOTE: Bootstrap spawns various commands with different working directories. @@ -2490,22 +2490,3 @@ fn find_correct_section_for_field(field_name: &str) -> Vec { }) .collect() } - -/// Resolve the build directory used for tests. -/// -/// - When tests are run through bootstrap (`x.py test`), the build system -/// sets `CARGO_TARGET_DIR`, so we can trust and use it here. -/// - When tests are run directly with cargo test, `CARGO_TARGET_DIR` will -/// not be set. In that case we fall back to resolving relative to -/// `CARGO_MANIFEST_DIR`, by walking two parents up and appending `build`. -fn test_build_dir() -> PathBuf { - env::var_os("CARGO_TARGET_DIR") - .map(|value| Path::new(&value).parent().unwrap().to_path_buf()) - .unwrap_or_else(|| { - let base = option_env!("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .unwrap_or_else(|| std::env::current_dir().expect("failed to get current dir")); - - base.ancestors().nth(2).unwrap_or_else(|| Path::new(".")).join("build") - }) -} From 191c7ed10f68c6891ba66e342fa58342e16db402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 25 Sep 2025 17:32:17 +0200 Subject: [PATCH 20/20] Make `cargo test` work again --- src/bootstrap/src/core/config/config.rs | 38 +++++++++++++++++++------ 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 74a272c740bd3..07a6616be0c98 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -414,15 +414,17 @@ impl Config { // Set config values based on flags. let mut exec_ctx = ExecutionContext::new(flags_verbose, flags_cmd.fail_fast()); exec_ctx.set_dry_run(if flags_dry_run { DryRun::UserSelected } else { DryRun::Disabled }); - let mut src = { + + let default_src_dir = { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); // Undo `src/bootstrap` manifest_dir.parent().unwrap().parent().unwrap().to_owned() }; - - if let Some(src_) = compute_src_directory(flags_src, &exec_ctx) { - src = src_; - } + let src = if let Some(s) = compute_src_directory(flags_src, &exec_ctx) { + s + } else { + default_src_dir.clone() + }; #[cfg(test)] { @@ -659,6 +661,10 @@ impl Config { out }; + let default_stage0_rustc_path = |dir: &Path| { + dir.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) + }; + if cfg!(test) { // When configuring bootstrap for tests, make sure to set the rustc and Cargo to the // same ones used to call the tests (if custom ones are not defined in the toml). If we @@ -667,6 +673,22 @@ impl Config { // Cargo in their bootstrap.toml. build_rustc = build_rustc.take().or(std::env::var_os("RUSTC").map(|p| p.into())); build_cargo = build_cargo.take().or(std::env::var_os("CARGO").map(|p| p.into())); + + // If we are running only `cargo test` (and not `x test bootstrap`), which is useful + // e.g. for debugging bootstrap itself, then we won't have RUSTC and CARGO set to the + // proper paths. + // We thus "guess" that the build directory is located at /build, and try to load + // rustc and cargo from there + let is_test_outside_x = std::env::var("CARGO_TARGET_DIR").is_err(); + if is_test_outside_x && build_rustc.is_none() { + let stage0_rustc = default_stage0_rustc_path(&default_src_dir.join("build")); + assert!( + stage0_rustc.exists(), + "Trying to run cargo test without having a stage0 rustc available in {}", + stage0_rustc.display() + ); + build_rustc = Some(stage0_rustc); + } } if !flags_skip_stage0_validation { @@ -700,7 +722,7 @@ impl Config { let initial_rustc = build_rustc.unwrap_or_else(|| { download_beta_toolchain(&dwn_ctx, &out); - out.join(host_target).join("stage0").join("bin").join(exe("rustc", host_target)) + default_stage0_rustc_path(&out) }); let initial_sysroot = t!(PathBuf::from_str( @@ -1540,11 +1562,11 @@ impl Config { println!("WARNING: CI rustc has some fields that are no longer supported in bootstrap; download-rustc will be disabled."); println!("HELP: Consider rebasing to a newer commit if available."); return None; - }, + } Err(e) => { eprintln!("ERROR: Failed to parse CI rustc bootstrap.toml: {e}"); exit!(2); - }, + } }; let current_config_toml = Self::get_toml(config_path).unwrap();