diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index a1f6fac8a51..f710c01ca33 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -20,7 +20,7 @@ use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; use crate::utils::channel; use crate::utils::helpers::{self, exe, get_clang_cl_resource_dir, output, t, up_to_date}; -use crate::{CLang, GitRepo, Kind}; +use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind}; use build_helper::ci::CiEnv; use build_helper::git::get_git_merge_base; @@ -105,8 +105,13 @@ pub fn prebuilt_llvm_config( let llvm_cmake_dir = out_dir.join("lib/cmake/llvm"); let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir }; + let smart_stamp_hash = generate_smart_stamp_hash( + &builder.config.src.join("src/llvm-project"), + &builder.in_tree_llvm_info.sha().unwrap_or_default(), + ); + let stamp = out_dir.join("llvm-finished-building"); - let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + let stamp = HashStamp::new(stamp, Some(&smart_stamp_hash)); if stamp.is_done() { if stamp.hash.is_none() { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 62ec208ee60..736a9b312f0 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -31,6 +31,7 @@ use build_helper::exit; use build_helper::util::fail; use filetime::FileTime; use once_cell::sync::OnceCell; +use sha2::digest::Digest; use termcolor::{ColorChoice, StandardStream, WriteColor}; use utils::channel::GitInfo; @@ -1918,3 +1919,45 @@ pub fn find_recent_config_change_ids(current_id: usize) -> Vec { .cloned() .collect() } + +/// Computes a hash representing the state of a repository/submodule and additional input. +/// +/// It uses `git diff` for the actual changes, and `git status` for including the untracked +/// files in the specified directory. The additional input is also incorporated into the +/// computation of the hash. +/// +/// # Parameters +/// +/// - `dir`: A reference to the directory path of the target repository/submodule. +/// - `additional_input`: An additional input to be included in the hash. +/// +/// # Panics +/// +/// In case of errors during `git` command execution (e.g., in tarball sources), default values +/// are used to prevent panics. +pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String { + let diff = Command::new("git") + .current_dir(dir) + .arg("diff") + .output() + .map(|o| String::from_utf8(o.stdout).unwrap_or_default()) + .unwrap_or_default(); + + let status = Command::new("git") + .current_dir(dir) + .arg("status") + .arg("--porcelain") + .arg("-z") + .arg("--untracked-files=normal") + .output() + .map(|o| String::from_utf8(o.stdout).unwrap_or_default()) + .unwrap_or_default(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(diff); + hasher.update(status); + hasher.update(additional_input); + + hex::encode(hasher.finalize().as_slice()) +}