bootstrap/utils/
build_stamp.rs
1use std::path::{Path, PathBuf};
6use std::{fs, io};
7
8use sha2::digest::Digest;
9
10use crate::core::builder::Builder;
11use crate::core::config::TargetSelection;
12use crate::utils::helpers::{hex_encode, mtime};
13use crate::{Compiler, Mode, helpers, t};
14
15#[cfg(test)]
16mod tests;
17
18#[derive(Clone)]
21pub struct BuildStamp {
22 path: PathBuf,
23 stamp: String,
24}
25
26impl BuildStamp {
27 pub fn new(dir: &Path) -> Self {
31 assert!(!dir.is_file(), "can't be a file path");
34 Self { path: dir.join(".stamp"), stamp: String::new() }
35 }
36
37 pub fn path(&self) -> &Path {
39 &self.path
40 }
41
42 pub fn stamp(&self) -> &str {
48 &self.stamp
49 }
50
51 pub fn add_stamp<S: ToString>(mut self, stamp: S) -> Self {
55 self.stamp.push_str(&stamp.to_string());
56 self
57 }
58
59 pub fn with_prefix(mut self, prefix: &str) -> Self {
63 assert!(
64 !prefix.starts_with('.') && !prefix.ends_with('.'),
65 "prefix can not start or end with '.'"
66 );
67
68 let stamp_filename = self.path.file_name().unwrap().to_str().unwrap();
69 let stamp_filename = stamp_filename.strip_prefix('.').unwrap_or(stamp_filename);
70 self.path.set_file_name(format!(".{prefix}-{stamp_filename}"));
71
72 self
73 }
74
75 pub fn remove(&self) -> io::Result<()> {
77 match fs::remove_file(&self.path) {
78 Ok(()) => Ok(()),
79 Err(e) => {
80 if e.kind() == io::ErrorKind::NotFound {
81 Ok(())
82 } else {
83 Err(e)
84 }
85 }
86 }
87 }
88
89 pub fn write(&self) -> io::Result<()> {
91 fs::write(&self.path, &self.stamp)
92 }
93
94 pub fn is_up_to_date(&self) -> bool {
98 match fs::read(&self.path) {
99 Ok(h) => self.stamp.as_bytes() == h.as_slice(),
100 Err(e) if e.kind() == io::ErrorKind::NotFound => false,
101 Err(e) => {
102 panic!("failed to read stamp file `{}`: {}", self.path.display(), e);
103 }
104 }
105 }
106}
107
108pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool {
112 let stamp = BuildStamp::new(dir);
113 let mut cleared = false;
114 if mtime(stamp.path()) < mtime(input) {
115 builder.verbose(|| println!("Dirty - {}", dir.display()));
116 let _ = fs::remove_dir_all(dir);
117 cleared = true;
118 } else if stamp.path().exists() {
119 return cleared;
120 }
121 t!(fs::create_dir_all(dir));
122 t!(fs::File::create(stamp.path()));
123 cleared
124}
125
126pub fn codegen_backend_stamp(
129 builder: &Builder<'_>,
130 compiler: Compiler,
131 target: TargetSelection,
132 backend: &str,
133) -> BuildStamp {
134 BuildStamp::new(&builder.cargo_out(compiler, Mode::Codegen, target))
135 .with_prefix(&format!("librustc_codegen_{backend}"))
136}
137
138pub fn libstd_stamp(
141 builder: &Builder<'_>,
142 compiler: Compiler,
143 target: TargetSelection,
144) -> BuildStamp {
145 BuildStamp::new(&builder.cargo_out(compiler, Mode::Std, target)).with_prefix("libstd")
146}
147
148pub fn librustc_stamp(
151 builder: &Builder<'_>,
152 compiler: Compiler,
153 target: TargetSelection,
154) -> BuildStamp {
155 BuildStamp::new(&builder.cargo_out(compiler, Mode::Rustc, target)).with_prefix("librustc")
156}
157
158pub fn generate_smart_stamp_hash(
174 builder: &Builder<'_>,
175 dir: &Path,
176 additional_input: &str,
177) -> String {
178 let diff = helpers::git(Some(dir))
179 .allow_failure()
180 .arg("diff")
181 .arg(".")
182 .run_capture_stdout(builder)
183 .stdout_if_ok()
184 .unwrap_or_default();
185
186 let status = helpers::git(Some(dir))
187 .allow_failure()
188 .arg("status")
189 .arg(".")
190 .arg("--porcelain")
191 .arg("-z")
192 .arg("--untracked-files=normal")
193 .run_capture_stdout(builder)
194 .stdout_if_ok()
195 .unwrap_or_default();
196
197 let mut hasher = sha2::Sha256::new();
198
199 hasher.update(diff);
200 hasher.update(status);
201 hasher.update(additional_input);
202
203 hex_encode(hasher.finalize().as_slice())
204}