|
10 | 10 |
|
11 | 11 | #![deny(warnings)]
|
12 | 12 |
|
| 13 | +extern crate filetime; |
| 14 | + |
| 15 | +use std::fs; |
13 | 16 | use std::process::{Command, Stdio};
|
14 | 17 | use std::path::{Path, PathBuf};
|
15 | 18 |
|
| 19 | +use filetime::FileTime; |
| 20 | + |
| 21 | +/// A helper macro to `unwrap` a result except also print out details like: |
| 22 | +/// |
| 23 | +/// * The file/line of the panic |
| 24 | +/// * The expression that failed |
| 25 | +/// * The error itself |
| 26 | +/// |
| 27 | +/// This is currently used judiciously throughout the build system rather than |
| 28 | +/// using a `Result` with `try!`, but this may change one day... |
| 29 | +#[macro_export] |
| 30 | +macro_rules! t { |
| 31 | + ($e:expr) => (match $e { |
| 32 | + Ok(e) => e, |
| 33 | + Err(e) => panic!("{} failed with {}", stringify!($e), e), |
| 34 | + }) |
| 35 | +} |
| 36 | + |
16 | 37 | pub fn run(cmd: &mut Command) {
|
17 | 38 | println!("running: {:?}", cmd);
|
18 | 39 | run_silent(cmd);
|
@@ -88,6 +109,56 @@ pub fn output(cmd: &mut Command) -> String {
|
88 | 109 | String::from_utf8(output.stdout).unwrap()
|
89 | 110 | }
|
90 | 111 |
|
| 112 | +pub fn rerun_if_changed_anything_in_dir(dir: &Path) { |
| 113 | + let mut stack = dir.read_dir().unwrap() |
| 114 | + .map(|e| e.unwrap()) |
| 115 | + .filter(|e| &*e.file_name() != ".git") |
| 116 | + .collect::<Vec<_>>(); |
| 117 | + while let Some(entry) = stack.pop() { |
| 118 | + let path = entry.path(); |
| 119 | + if entry.file_type().unwrap().is_dir() { |
| 120 | + stack.extend(path.read_dir().unwrap().map(|e| e.unwrap())); |
| 121 | + } else { |
| 122 | + println!("cargo:rerun-if-changed={}", path.display()); |
| 123 | + } |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +/// Returns the last-modified time for `path`, or zero if it doesn't exist. |
| 128 | +pub fn mtime(path: &Path) -> FileTime { |
| 129 | + fs::metadata(path).map(|f| { |
| 130 | + FileTime::from_last_modification_time(&f) |
| 131 | + }).unwrap_or(FileTime::zero()) |
| 132 | +} |
| 133 | + |
| 134 | +/// Returns whether `dst` is up to date given that the file or files in `src` |
| 135 | +/// are used to generate it. |
| 136 | +/// |
| 137 | +/// Uses last-modified time checks to verify this. |
| 138 | +pub fn up_to_date(src: &Path, dst: &Path) -> bool { |
| 139 | + let threshold = mtime(dst); |
| 140 | + let meta = match fs::metadata(src) { |
| 141 | + Ok(meta) => meta, |
| 142 | + Err(e) => panic!("source {:?} failed to get metadata: {}", src, e), |
| 143 | + }; |
| 144 | + if meta.is_dir() { |
| 145 | + dir_up_to_date(src, &threshold) |
| 146 | + } else { |
| 147 | + FileTime::from_last_modification_time(&meta) <= threshold |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool { |
| 152 | + t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| { |
| 153 | + let meta = t!(e.metadata()); |
| 154 | + if meta.is_dir() { |
| 155 | + dir_up_to_date(&e.path(), threshold) |
| 156 | + } else { |
| 157 | + FileTime::from_last_modification_time(&meta) < *threshold |
| 158 | + } |
| 159 | + }) |
| 160 | +} |
| 161 | + |
91 | 162 | fn fail(s: &str) -> ! {
|
92 | 163 | println!("\n\n{}\n\n", s);
|
93 | 164 | std::process::exit(1);
|
|
0 commit comments