about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs9
-rw-r--r--src/bootstrap/src/lib.rs43
2 files changed, 50 insertions, 2 deletions
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 a57b09e2ef6..c6e279ca9d8 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;
 
@@ -1872,3 +1873,45 @@ pub fn find_recent_config_change_ids(current_id: usize) -> Vec<usize> {
         .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())
+}