From f20acc9e7e3aef051f72382c2b7196677f893c39 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 23 Mar 2026 10:03:50 +0100 Subject: [PATCH 1/3] bootstrap: Print why `if-unchanged` isn't downloading rustc Example output: ``` $ x b compiler Building bootstrap Finished `dev` profile [unoptimized] target(s) in 0.02s NOTE: detected 3 modifications that could affect a build of rustc - src/bootstrap/src/core/config/config.rs - src/bootstrap/src/core/download.rs - src/build_helper/src/git.rs skipping rustc download due to `download-rustc = 'if-unchanged'` Building stage1 compiler artifacts (stage0 -> stage1, aarch64-apple-darwin) Finished `release` profile [optimized] target(s) in 0.72s Creating a sysroot for stage1 compiler (use `rustup toolchain link 'name' build/host/stage1`) Build completed successfully in 0:00:05 ``` --- src/bootstrap/src/core/config/config.rs | 24 +++++++++++++++---- src/bootstrap/src/core/config/tests.rs | 30 +++++++++++------------- src/bootstrap/src/core/download.rs | 2 +- src/build_helper/src/git.rs | 31 ++++++++++++++++++------- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2c3b5251e12f6..e685226570d9d 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2237,11 +2237,7 @@ pub fn download_ci_rustc_commit<'a>( }); match freshness { PathFreshness::LastModifiedUpstream { upstream } => upstream, - PathFreshness::HasLocalModifications { upstream } => { - if if_unchanged { - return None; - } - + PathFreshness::HasLocalModifications { upstream, modifications } => { if dwn_ctx.is_running_on_ci() { eprintln!("CI rustc commit matches with HEAD and we are in CI."); eprintln!( @@ -2250,6 +2246,24 @@ pub fn download_ci_rustc_commit<'a>( return None; } + eprintln!( + "NOTE: detected {} modifications that could affect a build of rustc", + modifications.len() + ); + for file in modifications.iter().take(10) { + eprintln!("- {}", file.display()); + } + if modifications.len() > 10 { + eprintln!("- ..."); + } + + if if_unchanged { + eprintln!("skipping rustc download due to `download-rustc = 'if-unchanged'`"); + return None; + } else { + eprintln!("downloading anyway due to `download-rustc = true`"); + } + upstream } PathFreshness::MissingUpstream => { diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 277ede8d77456..c281aa94f64e6 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -32,6 +32,13 @@ fn get_toml(file: &Path) -> Result { toml::from_str(&contents).and_then(|table: toml::Value| TomlConfig::deserialize(table)) } +fn modified(upstream: impl Into, changes: &[&str]) -> PathFreshness { + PathFreshness::HasLocalModifications { + upstream: upstream.into(), + modifications: changes.iter().copied().map(PathBuf::from).collect(), + } +} + #[test] fn download_ci_llvm() { let config = TestCtx::new().config("check").create_config(); @@ -692,7 +699,7 @@ fn test_pr_ci_changed_in_pr() { let sha = ctx.create_upstream_merge(&["a"]); ctx.create_nonupstream_merge(&["b"]); let src = ctx.check_modifications(&["b"], CiEnv::GitHubActions); - assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha }); + assert_eq!(src, modified(sha, &["b"])); }); } @@ -712,7 +719,7 @@ fn test_auto_ci_changed_in_pr() { let sha = ctx.create_upstream_merge(&["a"]); ctx.create_upstream_merge(&["b", "c"]); let src = ctx.check_modifications(&["c", "d"], CiEnv::GitHubActions); - assert_eq!(src, PathFreshness::HasLocalModifications { upstream: sha }); + assert_eq!(src, modified(sha, &["c"])); }); } @@ -723,10 +730,7 @@ fn test_local_uncommitted_modifications() { ctx.create_branch("feature"); ctx.modify("a"); - assert_eq!( - ctx.check_modifications(&["a", "d"], CiEnv::None), - PathFreshness::HasLocalModifications { upstream: sha } - ); + assert_eq!(ctx.check_modifications(&["a", "d"], CiEnv::None), modified(sha, &["a"]),); }); } @@ -741,10 +745,7 @@ fn test_local_committed_modifications() { ctx.modify("a"); ctx.commit(); - assert_eq!( - ctx.check_modifications(&["a", "d"], CiEnv::None), - PathFreshness::HasLocalModifications { upstream: sha } - ); + assert_eq!(ctx.check_modifications(&["a", "d"], CiEnv::None), modified(sha, &["a"]),); }); } @@ -757,10 +758,7 @@ fn test_local_committed_modifications_subdirectory() { ctx.modify("a/b/d"); ctx.commit(); - assert_eq!( - ctx.check_modifications(&["a/b"], CiEnv::None), - PathFreshness::HasLocalModifications { upstream: sha } - ); + assert_eq!(ctx.check_modifications(&["a/b"], CiEnv::None), modified(sha, &["a/b/d"]),); }); } @@ -836,11 +834,11 @@ fn test_local_changes_negative_path() { ); assert_eq!( ctx.check_modifications(&[":!c"], CiEnv::None), - PathFreshness::HasLocalModifications { upstream: upstream.clone() } + modified(&upstream, &["b", "d"]), ); assert_eq!( ctx.check_modifications(&[":!d", ":!x"], CiEnv::None), - PathFreshness::HasLocalModifications { upstream } + modified(&upstream, &["b"]), ); }); } diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 389956f145994..b8e00c596f282 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -266,7 +266,7 @@ impl Config { }); let llvm_sha = match llvm_freshness { PathFreshness::LastModifiedUpstream { upstream } => upstream, - PathFreshness::HasLocalModifications { upstream } => upstream, + PathFreshness::HasLocalModifications { upstream, modifications: _ } => upstream, PathFreshness::MissingUpstream => { eprintln!("error: could not find commit hash for downloading LLVM"); eprintln!("HELP: maybe your repository history is too shallow?"); diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 330fb465de42c..87a52eee49b85 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use crate::ci::CiEnv; @@ -38,7 +38,7 @@ pub enum PathFreshness { /// "Local" essentially means "not-upstream" here. /// `upstream` is the latest upstream merge commit that made modifications to the /// set of paths. - HasLocalModifications { upstream: String }, + HasLocalModifications { upstream: String, modifications: Vec }, /// No upstream commit was found. /// This should not happen in most reasonable circumstances, but one never knows. MissingUpstream, @@ -134,21 +134,34 @@ pub fn check_path_modifications( // However, that should be equivalent to checking if something has changed // from the latest upstream commit *that modified `target_paths`*, and // with this approach we do not need to invoke git an additional time. - if has_changed_since(git_dir, &upstream_sha, target_paths) { - Ok(PathFreshness::HasLocalModifications { upstream: upstream_sha }) + let modifications = changes_since(git_dir, &upstream_sha, target_paths)?; + if !modifications.is_empty() { + Ok(PathFreshness::HasLocalModifications { upstream: upstream_sha, modifications }) } else { Ok(PathFreshness::LastModifiedUpstream { upstream: upstream_sha }) } } /// Returns true if any of the passed `paths` have changed since the `base` commit. -pub fn has_changed_since(git_dir: &Path, base: &str, paths: &[&str]) -> bool { +pub fn changes_since(git_dir: &Path, base: &str, paths: &[&str]) -> Result, String> { + use std::io::BufRead; + run_git_diff_index(Some(git_dir), |cmd| { - cmd.args(["--quiet", base, "--"]).args(paths); + cmd.args([base, "--name-only", "--"]).args(paths); + + let output = cmd.stderr(Stdio::inherit()).output().expect("cannot run git diff-index"); + if !output.status.success() { + return Err(format!("failed to run: {cmd:?}: {:?}", output.status)); + } - // Exit code 0 => no changes - // Exit code 1 => some changes were detected - !cmd.status().expect("cannot run git diff-index").success() + output + .stdout + .lines() + .map(|res| match res { + Ok(line) => Ok(PathBuf::from(line)), + Err(e) => Err(format!("invalid UTF-8 in diff-index: {e:?}")), + }) + .collect() }) } From 01005bc234c39b5b2c39da2d24dc55562c894017 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 23 Mar 2026 10:05:21 +0100 Subject: [PATCH 2/3] Don't consider `bootstrap/defaults` to invalidate the rustc cache Anything that can change there can also be changed in bootstrap.toml. We have a separate check to make sure that the local config is compatable with CI's config. --- src/bootstrap/src/core/config/config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index e685226570d9d..f947ba7002963 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -78,6 +78,7 @@ pub const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[ ":!src/rustdoc-json-types", ":!tests", ":!triagebot.toml", + ":!src/bootstrap/defaults", ]; /// Global configuration for the entire build and/or bootstrap. From 0dde8fdbcfcf09b170f1fbe67cf5c7eb0825eced Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 23 Mar 2026 10:06:15 +0100 Subject: [PATCH 3/3] Switch `download-rustc` back on by default for profile = library This was turned off because `x test --set rust.download-rustc=true --stage=1 library/std` has been broken for years. The workaround is simple and it's to remove `test-stage = 1` from the `library` profile defaults. --- src/bootstrap/defaults/bootstrap.library.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/bootstrap/defaults/bootstrap.library.toml b/src/bootstrap/defaults/bootstrap.library.toml index 3f811402b26ff..858cd7d3d591f 100644 --- a/src/bootstrap/defaults/bootstrap.library.toml +++ b/src/bootstrap/defaults/bootstrap.library.toml @@ -1,8 +1,4 @@ # These defaults are meant for contributors to the standard library and documentation. -[build] -bench-stage = 1 -test-stage = 1 - [rust] # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. incremental = true @@ -12,8 +8,7 @@ lto = "off" # # If compiler-affecting directories are not modified, use precompiled rustc to speed up # library development by skipping compiler builds. -# FIXME: download-rustc is currently broken: https://github.com/rust-lang/rust/issues/142505 -download-rustc = false +download-rustc = "if-unchanged" [llvm] # Will download LLVM from CI if available on your platform.