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:rustc-env=CCTOP_CACHE_HASH={:016x}",
digest(&read_all(&files))
);
}
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);
}