|
7 | 7 | use std::collections::hash_map::DefaultHasher;
|
8 | 8 | use std::env;
|
9 | 9 | use std::ffi::{OsStr, OsString};
|
10 |
| -use std::fs; |
| 10 | +use std::fs::{self, File}; |
11 | 11 | use std::hash::{Hash, Hasher};
|
| 12 | +use std::io::{BufRead, BufReader}; |
12 | 13 | use std::ops::Not;
|
13 | 14 | use std::path::{Path, PathBuf};
|
14 | 15 | use std::process::Command;
|
15 | 16 |
|
16 | 17 | use anyhow::{bail, Context, Result};
|
17 | 18 | use tempfile::TempDir;
|
| 19 | +use walkdir::WalkDir; |
18 | 20 |
|
19 | 21 | /// Returns where the given rustc stores its sysroot source code.
|
20 | 22 | pub fn rustc_sysroot_src(mut rustc: Command) -> Result<PathBuf> {
|
@@ -84,6 +86,30 @@ fn make_writeable(p: &Path) -> Result<()> {
|
84 | 86 | Ok(())
|
85 | 87 | }
|
86 | 88 |
|
| 89 | +/// Hash the contents of every file in a directory, recursively. |
| 90 | +pub fn hash_recursive(path: &Path, hasher: &mut DefaultHasher) -> Result<()> { |
| 91 | + for entry in WalkDir::new(path).follow_links(true).into_iter() { |
| 92 | + let entry = entry?; |
| 93 | + // WalkDir yields the directories as well, and File::open will succeed on them. The |
| 94 | + // reliable way to distinguish directories here is to check explicitly. |
| 95 | + if entry.file_type().is_dir() { |
| 96 | + continue; |
| 97 | + } |
| 98 | + let file = File::open(entry.path())?; |
| 99 | + let mut reader = BufReader::new(file); |
| 100 | + while let Ok(buf) = reader.fill_buf() { |
| 101 | + if buf.is_empty() { |
| 102 | + // EOF is not an error, we just get an empty buffer. |
| 103 | + break; |
| 104 | + } |
| 105 | + hasher.write(buf); |
| 106 | + let len = buf.len(); |
| 107 | + reader.consume(len); |
| 108 | + } |
| 109 | + } |
| 110 | + Ok(()) |
| 111 | +} |
| 112 | + |
87 | 113 | /// The build mode to use for this sysroot.
|
88 | 114 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
89 | 115 | pub enum BuildMode {
|
@@ -220,18 +246,16 @@ impl SysrootBuilder {
|
220 | 246 | &self,
|
221 | 247 | src_dir: &Path,
|
222 | 248 | rustc_version: &rustc_version::VersionMeta,
|
223 |
| - ) -> u64 { |
| 249 | + ) -> Result<u64> { |
224 | 250 | let mut hasher = DefaultHasher::new();
|
225 | 251 |
|
226 |
| - // For now, we just hash in the information we have in `self`. |
227 |
| - // Ideally we'd recursively hash the entire folder but that sounds slow? |
228 |
| - src_dir.hash(&mut hasher); |
| 252 | + hash_recursive(src_dir, &mut hasher)?; |
229 | 253 | self.config.hash(&mut hasher);
|
230 | 254 | self.mode.hash(&mut hasher);
|
231 | 255 | self.rustflags.hash(&mut hasher);
|
232 | 256 | rustc_version.hash(&mut hasher);
|
233 | 257 |
|
234 |
| - hasher.finish() |
| 258 | + Ok(hasher.finish()) |
235 | 259 | }
|
236 | 260 |
|
237 | 261 | fn sysroot_read_hash(&self) -> Option<u64> {
|
@@ -372,7 +396,7 @@ path = "lib.rs"
|
372 | 396 | }
|
373 | 397 |
|
374 | 398 | // Check if we even need to do anything.
|
375 |
| - let cur_hash = self.sysroot_compute_hash(src_dir, &rustc_version); |
| 399 | + let cur_hash = self.sysroot_compute_hash(src_dir, &rustc_version)?; |
376 | 400 | if self.sysroot_read_hash() == Some(cur_hash) {
|
377 | 401 | // Already done!
|
378 | 402 | return Ok(());
|
|
0 commit comments