From 0157d0643f4f0a550399b84c1f35f9561abae609 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 10 Feb 2024 17:15:15 -0500 Subject: [PATCH] Hash the mtime and size of every file in the sysroot --- Cargo.lock | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/lib.rs | 33 ++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a84104e..203ed51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,7 @@ dependencies = [ "anyhow", "rustc_version", "tempfile", + "walkdir", ] [[package]] @@ -94,6 +95,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "semver" version = "1.0.21" @@ -113,6 +123,47 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 9d1f58c..d88c29e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ description = "Build a rustc sysroot with custom flags" tempfile = "3" rustc_version = "0.4" anyhow = "1.0" +walkdir = "2.4" diff --git a/src/lib.rs b/src/lib.rs index a01e323..252d654 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ use std::process::Command; use anyhow::{bail, Context, Result}; use tempfile::TempDir; +use walkdir::WalkDir; /// Returns where the given rustc stores its sysroot source code. pub fn rustc_sysroot_src(mut rustc: Command) -> Result { @@ -84,6 +85,29 @@ fn make_writeable(p: &Path) -> Result<()> { Ok(()) } +/// Hash the metadata and size of every file in a directory, recursively. +pub fn hash_recursive(path: &Path, hasher: &mut DefaultHasher) -> Result<()> { + // We sort the entries to ensure a stable hash. + for entry in WalkDir::new(path) + .follow_links(true) + .sort_by_file_name() + .into_iter() + { + let entry = entry?; + // WalkDir yields the directories as well, and File::open will succeed on them. The + // reliable way to distinguish directories here is to check explicitly. + if entry.file_type().is_dir() { + continue; + } + let meta = entry.metadata()?; + // Hashing the mtime and file size should catch basically all mutations, + // and is faster than hashing the file contents. + meta.modified()?.hash(hasher); + meta.len().hash(hasher); + } + Ok(()) +} + /// The build mode to use for this sysroot. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum BuildMode { @@ -220,18 +244,17 @@ impl SysrootBuilder { &self, src_dir: &Path, rustc_version: &rustc_version::VersionMeta, - ) -> u64 { + ) -> Result { let mut hasher = DefaultHasher::new(); - // For now, we just hash in the information we have in `self`. - // Ideally we'd recursively hash the entire folder but that sounds slow? src_dir.hash(&mut hasher); + hash_recursive(src_dir, &mut hasher)?; self.config.hash(&mut hasher); self.mode.hash(&mut hasher); self.rustflags.hash(&mut hasher); rustc_version.hash(&mut hasher); - hasher.finish() + Ok(hasher.finish()) } fn sysroot_read_hash(&self) -> Option { @@ -372,7 +395,7 @@ path = "lib.rs" } // Check if we even need to do anything. - let cur_hash = self.sysroot_compute_hash(src_dir, &rustc_version); + let cur_hash = self.sysroot_compute_hash(src_dir, &rustc_version)?; if self.sysroot_read_hash() == Some(cur_hash) { // Already done! return Ok(());