From 2b20750f189f36f986a69b07841f3fef2298243f Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 10 Feb 2024 17:15:15 -0500 Subject: [PATCH] Hash the sysroot contents --- Cargo.lock | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/lib.rs | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 83 insertions(+), 7 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..eca9124 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,14 +7,16 @@ use std::collections::hash_map::DefaultHasher; use std::env; use std::ffi::{OsStr, OsString}; -use std::fs; +use std::fs::{self, File}; use std::hash::{Hash, Hasher}; +use std::io::{BufRead, BufReader}; use std::ops::Not; use std::path::{Path, PathBuf}; 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 +86,30 @@ fn make_writeable(p: &Path) -> Result<()> { Ok(()) } +/// Hash the contents of every file in a directory, recursively. +pub fn hash_recursive(path: &Path, hasher: &mut DefaultHasher) -> Result<()> { + for entry in WalkDir::new(path).follow_links(true).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.metadata()?.is_dir() { + continue; + } + let file = File::open(entry.path())?; + let mut reader = BufReader::new(file); + while let Ok(buf) = reader.fill_buf() { + if buf.is_empty() { + // EOF is not an error, we just get an empty buffer. + break; + } + hasher.write(buf); + let len = buf.len(); + reader.consume(len); + } + } + Ok(()) +} + /// The build mode to use for this sysroot. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum BuildMode { @@ -220,18 +246,16 @@ 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 +396,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(());