use std::path::{Path, PathBuf};
pub const ROOTS: &[&str] = &[
"src/session",
"src/cache.rs",
"src/cache",
"src/pricing.rs",
"src/config.rs",
];
fn main() {
let (files, dirs) = sources();
for path in files.iter().chain(&dirs) {
println!("cargo:rerun-if-changed={}", slash_path(path));
}
println!("cargo:rerun-if-env-changed=CCTOP_CHECKOUT");
println!(
"cargo:rustc-env=CCTOP_CACHE_HASH={:016x}",
digest(&read_all(&files))
);
println!(
"cargo:rustc-env=CCTOP_COMMIT={}",
commit().unwrap_or_default()
);
}
pub fn commit() -> Option<String> {
let git = |args: &[&str]| -> Option<String> {
let out = std::process::Command::new("git").args(args).output().ok()?;
let text = String::from_utf8_lossy(&out.stdout).trim().to_string();
(out.status.success() && !text.is_empty()).then_some(text)
};
if let Some(hash) = git(&["rev-parse", "--short=9", "HEAD"]) {
let mut watched = vec!["HEAD".to_string(), "packed-refs".to_string()];
watched.extend(git(&["symbolic-ref", "-q", "HEAD"]));
for name in watched {
if let Some(path) = git(&["rev-parse", "--git-path", &name])
&& Path::new(&path).exists()
{
println!("cargo:rerun-if-changed={path}");
}
}
return Some(hash);
}
let info = std::fs::read_to_string(".cargo_vcs_info.json").ok()?;
let at = info.find("\"sha1\"")?;
let hash: String = info[at + 6..]
.chars()
.skip_while(|c| !c.is_ascii_hexdigit())
.take_while(char::is_ascii_hexdigit)
.take(9)
.collect();
(hash.len() == 9).then_some(hash)
}
pub fn sources() -> (Vec<PathBuf>, Vec<PathBuf>) {
let (mut files, mut dirs) = (Vec::new(), Vec::new());
for root in ROOTS {
collect(Path::new(root), &mut files, &mut dirs);
}
files.sort();
dirs.sort();
(files, dirs)
}
pub fn collect(path: &Path, files: &mut Vec<PathBuf>, dirs: &mut Vec<PathBuf>) {
if path.is_dir() {
dirs.push(path.to_path_buf());
let Ok(rd) = std::fs::read_dir(path) else {
return;
};
for entry in rd.flatten() {
collect(&entry.path(), files, dirs);
}
} else if path.is_file() {
files.push(path.to_path_buf());
}
}
pub fn read_all(files: &[PathBuf]) -> Vec<(String, Vec<u8>)> {
files
.iter()
.map(|f| {
let bytes = std::fs::read(f).unwrap_or_else(|_| b"<unreadable>".to_vec());
(slash_path(f), bytes)
})
.collect()
}
pub fn digest(entries: &[(String, Vec<u8>)]) -> u64 {
let mut hash = FNV_OFFSET;
for (path, bytes) in entries {
write(&mut hash, path.as_bytes());
write(&mut hash, bytes);
}
hash
}
pub fn slash_path(path: &Path) -> String {
path.to_string_lossy().replace('\\', "/")
}
pub const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
pub const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
pub fn write(hash: &mut u64, bytes: &[u8]) {
for b in bytes {
*hash ^= u64::from(*b);
*hash = hash.wrapping_mul(FNV_PRIME);
}
*hash ^= bytes.len() as u64;
*hash = hash.wrapping_mul(FNV_PRIME);
}