Skip to content

Commit ed50c8a

Browse files
committed
Make dev serve actually detect all updates
1 parent fc8bf97 commit ed50c8a

File tree

1 file changed

+71
-38
lines changed

1 file changed

+71
-38
lines changed

clippy_dev/src/serve.rs

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
use crate::utils::{ErrAction, expect_action};
2+
use core::fmt::Display;
3+
use core::mem;
14
use std::path::Path;
25
use std::process::Command;
36
use std::time::{Duration, SystemTime};
4-
use std::{env, thread};
7+
use std::{env, fs, thread};
8+
use walkdir::WalkDir;
59

610
#[cfg(windows)]
711
const PYTHON: &str = "python";
@@ -18,56 +22,85 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
1822
Some(lint) => format!("http://localhost:{port}/#{lint}"),
1923
});
2024

25+
let mut last_update = mtime("util/gh-pages/index.html");
2126
loop {
22-
let index_time = mtime("util/gh-pages/index.html");
23-
let times = [
24-
"clippy_lints/src",
25-
"util/gh-pages/index_template.html",
26-
"tests/compile-test.rs",
27-
]
28-
.map(mtime);
29-
30-
if times.iter().any(|&time| index_time < time) {
31-
Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
32-
.arg("collect-metadata")
33-
.spawn()
34-
.unwrap()
35-
.wait()
36-
.unwrap();
27+
if is_metadata_outdated(mem::replace(&mut last_update, SystemTime::now())) {
28+
// Ignore the command result; we'll fall back to displaying the old metadata.
29+
let _ = expect_action(
30+
Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
31+
.arg("collect-metadata")
32+
.status(),
33+
ErrAction::Run,
34+
"cargo collect-metadata",
35+
);
36+
last_update = SystemTime::now();
3737
}
38+
39+
// Only start the web server the first time around.
3840
if let Some(url) = url.take() {
3941
thread::spawn(move || {
40-
let mut child = Command::new(PYTHON)
41-
.arg("-m")
42-
.arg("http.server")
43-
.arg(port.to_string())
44-
.current_dir("util/gh-pages")
45-
.spawn()
46-
.unwrap();
42+
let mut child = expect_action(
43+
Command::new(PYTHON)
44+
.args(["-m", "http.server", port.to_string().as_str()])
45+
.current_dir("util/gh-pages")
46+
.spawn(),
47+
ErrAction::Run,
48+
"python -m http.server",
49+
);
4750
// Give some time for python to start
4851
thread::sleep(Duration::from_millis(500));
4952
// Launch browser after first export.py has completed and http.server is up
5053
let _result = opener::open(url);
51-
child.wait().unwrap();
54+
expect_action(child.wait(), ErrAction::Run, "python -m http.server");
5255
});
5356
}
57+
58+
// Delay to avoid updating the metadata too aggressively.
5459
thread::sleep(Duration::from_millis(1000));
5560
}
5661
}
5762

58-
fn mtime(path: impl AsRef<Path>) -> SystemTime {
59-
let path = path.as_ref();
60-
if path.is_dir() {
61-
path.read_dir()
62-
.into_iter()
63-
.flatten()
64-
.flatten()
65-
.map(|entry| mtime(entry.path()))
66-
.max()
67-
.unwrap_or(SystemTime::UNIX_EPOCH)
68-
} else {
69-
path.metadata()
70-
.and_then(|metadata| metadata.modified())
71-
.unwrap_or(SystemTime::UNIX_EPOCH)
63+
fn log_err_and_continue<T>(res: Result<T, impl Display>, path: &Path) -> Option<T> {
64+
match res {
65+
Ok(x) => Some(x),
66+
Err(ref e) => {
67+
eprintln!("error reading `{}`: {e}", path.display());
68+
None
69+
},
7270
}
7371
}
72+
73+
fn mtime(path: &str) -> SystemTime {
74+
log_err_and_continue(fs::metadata(path), path.as_ref())
75+
.and_then(|metadata| log_err_and_continue(metadata.modified(), path.as_ref()))
76+
.unwrap_or(SystemTime::UNIX_EPOCH)
77+
}
78+
79+
fn is_metadata_outdated(time: SystemTime) -> bool {
80+
// Ignore all IO errors here. We don't want to stop them from hosting the server.
81+
if time < mtime("util/gh-pages/index_template.html") || time < mtime("tests/compile-test.rs") {
82+
return true;
83+
}
84+
let Some(dir) = log_err_and_continue(fs::read_dir("."), ".".as_ref()) else {
85+
return false;
86+
};
87+
dir.map_while(|e| log_err_and_continue(e, ".".as_ref())).any(|e| {
88+
let name = e.file_name();
89+
let name_bytes = name.as_encoded_bytes();
90+
if (name_bytes.starts_with(b"clippy_lints") && name_bytes != b"clippy_lints_internal")
91+
|| name_bytes == b"clippy_config"
92+
{
93+
WalkDir::new(&name)
94+
.into_iter()
95+
.map_while(|e| log_err_and_continue(e, name.as_ref()))
96+
.filter(|e| e.file_type().is_file())
97+
.filter_map(|e| {
98+
log_err_and_continue(e.metadata(), e.path())
99+
.and_then(|m| log_err_and_continue(m.modified(), e.path()))
100+
})
101+
.any(|ftime| time < ftime)
102+
} else {
103+
false
104+
}
105+
})
106+
}

0 commit comments

Comments
 (0)