Skip to content

Commit b84e625

Browse files
committed
Auto merge of #6484 - Eh2406:modified-during-build, r=alexcrichton
Rebuild on mid build file modification This is an attempt to Fixes #2426, it is based on the test @dwijnand made for #5417.
2 parents 64f0db3 + 40a0779 commit b84e625

File tree

6 files changed

+140
-8
lines changed

6 files changed

+140
-8
lines changed

src/cargo/core/compiler/custom_build.rs

+2
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
332332
state.build_plan(invocation_name, cmd.clone(), Arc::new(Vec::new()));
333333
} else {
334334
state.running(&cmd);
335+
let timestamp = paths::get_current_filesystem_time(&output_file)?;
335336
let output = if extra_verbose {
336337
let prefix = format!("[{} {}] ", id.name(), id.version());
337338
state.capture_output(&cmd, Some(prefix), true)
@@ -354,6 +355,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
354355
// state informing what variables were discovered via our script as
355356
// well.
356357
paths::write(&output_file, &output.stdout)?;
358+
filetime::set_file_times(output_file, timestamp, timestamp)?;
357359
paths::write(&err_file, &output.stderr)?;
358360
paths::write(&root_output_file, util::path2bytes(&script_out_dir)?)?;
359361
let parsed_output =

src/cargo/core/compiler/fingerprint.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -761,23 +761,25 @@ where
761761
}
762762
};
763763

764-
// Note that equal mtimes are considered "stale". For filesystems with
765-
// not much timestamp precision like 1s this is a conservative approximation
764+
// TODO: fix #5918
765+
// Note that equal mtimes should be considered "stale". For filesystems with
766+
// not much timestamp precision like 1s this is would be a conservative approximation
766767
// to handle the case where a file is modified within the same second after
767-
// a build finishes. We want to make sure that incremental rebuilds pick that up!
768+
// a build starts. We want to make sure that incremental rebuilds pick that up!
768769
//
769770
// For filesystems with nanosecond precision it's been seen in the wild that
770771
// its "nanosecond precision" isn't really nanosecond-accurate. It turns out that
771772
// kernels may cache the current time so files created at different times actually
772773
// list the same nanosecond precision. Some digging on #5919 picked up that the
773774
// kernel caches the current time between timer ticks, which could mean that if
774-
// a file is updated at most 10ms after a build finishes then Cargo may not
775+
// a file is updated at most 10ms after a build starts then Cargo may not
775776
// pick up the build changes.
776777
//
777-
// All in all, the equality check here is a conservative assumption that,
778+
// All in all, an equality check here would be a conservative assumption that,
778779
// if equal, files were changed just after a previous build finished.
779-
// It's hoped this doesn't cause too many issues in practice!
780-
if mtime2 >= mtime {
780+
// Unfortunately this became problematic when (in #6484) cargo switch to more accurately
781+
// measuring the start time of builds.
782+
if mtime2 > mtime {
781783
info!("stale: {} -- {} vs {}", path.display(), mtime2, mtime);
782784
true
783785
} else {

src/cargo/core/compiler/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ fn rustc<'a, 'cfg>(
292292
}
293293

294294
state.running(&rustc);
295+
let timestamp = paths::get_current_filesystem_time(&dep_info_loc)?;
295296
if json_messages {
296297
exec.exec_json(
297298
rustc,
@@ -334,6 +335,7 @@ fn rustc<'a, 'cfg>(
334335
rustc_dep_info_loc.display()
335336
))
336337
})?;
338+
filetime::set_file_times(dep_info_loc, timestamp, timestamp)?;
337339
}
338340

339341
Ok(())

src/cargo/util/paths.rs

+12
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ pub fn mtime(path: &Path) -> CargoResult<FileTime> {
197197
Ok(FileTime::from_last_modification_time(&meta))
198198
}
199199

200+
/// get `FileTime::from_system_time(SystemTime::now());` using the exact clock that this file system is using.
201+
pub fn get_current_filesystem_time(path: &Path) -> CargoResult<FileTime> {
202+
// note that if `FileTime::from_system_time(SystemTime::now());` is determined to be sufficient,
203+
// then this can be removed.
204+
let timestamp = path.with_file_name("invoked.timestamp");
205+
write(
206+
&timestamp,
207+
b"This file has an mtime of when this was started.",
208+
)?;
209+
Ok(mtime(&timestamp)?)
210+
}
211+
200212
#[cfg(unix)]
201213
pub fn path2bytes(path: &Path) -> CargoResult<&[u8]> {
202214
use std::os::unix::prelude::*;

tests/testsuite/freshness.rs

+113-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use std::fs::{self, File};
1+
use std::fs::{self, File, OpenOptions};
22
use std::io::prelude::*;
3+
use std::net::TcpListener;
4+
use std::thread;
35

46
use crate::support::paths::CargoPathExt;
57
use crate::support::registry::Package;
@@ -1304,6 +1306,9 @@ fn bust_patched_dep() {
13041306
.build();
13051307

13061308
p.cargo("build").run();
1309+
if is_coarse_mtime() {
1310+
sleep_ms(1000);
1311+
}
13071312

13081313
File::create(&p.root().join("reg1new/src/lib.rs")).unwrap();
13091314
if is_coarse_mtime() {
@@ -1332,3 +1337,110 @@ fn bust_patched_dep() {
13321337
)
13331338
.run();
13341339
}
1340+
1341+
#[test]
1342+
fn rebuild_on_mid_build_file_modification() {
1343+
let server = TcpListener::bind("127.0.0.1:0").unwrap();
1344+
let addr = server.local_addr().unwrap();
1345+
1346+
let p = project()
1347+
.file(
1348+
"Cargo.toml",
1349+
r#"
1350+
[workspace]
1351+
members = ["root", "proc_macro_dep"]
1352+
"#,
1353+
)
1354+
.file(
1355+
"root/Cargo.toml",
1356+
r#"
1357+
[project]
1358+
name = "root"
1359+
version = "0.1.0"
1360+
authors = []
1361+
1362+
[dependencies]
1363+
proc_macro_dep = { path = "../proc_macro_dep" }
1364+
"#,
1365+
)
1366+
.file(
1367+
"root/src/lib.rs",
1368+
r#"
1369+
#[macro_use]
1370+
extern crate proc_macro_dep;
1371+
1372+
#[derive(Noop)]
1373+
pub struct X;
1374+
"#,
1375+
)
1376+
.file(
1377+
"proc_macro_dep/Cargo.toml",
1378+
r#"
1379+
[project]
1380+
name = "proc_macro_dep"
1381+
version = "0.1.0"
1382+
authors = []
1383+
1384+
[lib]
1385+
proc-macro = true
1386+
"#,
1387+
)
1388+
.file(
1389+
"proc_macro_dep/src/lib.rs",
1390+
&format!(
1391+
r#"
1392+
extern crate proc_macro;
1393+
1394+
use std::io::Read;
1395+
use std::net::TcpStream;
1396+
use proc_macro::TokenStream;
1397+
1398+
#[proc_macro_derive(Noop)]
1399+
pub fn noop(_input: TokenStream) -> TokenStream {{
1400+
let mut stream = TcpStream::connect("{}").unwrap();
1401+
let mut v = Vec::new();
1402+
stream.read_to_end(&mut v).unwrap();
1403+
"".parse().unwrap()
1404+
}}
1405+
"#,
1406+
addr
1407+
),
1408+
)
1409+
.build();
1410+
let root = p.root();
1411+
1412+
let t = thread::spawn(move || {
1413+
let socket = server.accept().unwrap().0;
1414+
sleep_ms(1000);
1415+
let mut file = OpenOptions::new()
1416+
.write(true)
1417+
.append(true)
1418+
.open(root.join("root/src/lib.rs"))
1419+
.unwrap();
1420+
writeln!(file, "// modified").expect("Failed to append to root sources");
1421+
drop(file);
1422+
drop(socket);
1423+
drop(server.accept().unwrap());
1424+
});
1425+
1426+
p.cargo("build")
1427+
.with_stderr(
1428+
"\
1429+
[COMPILING] proc_macro_dep v0.1.0 ([..]/proc_macro_dep)
1430+
[COMPILING] root v0.1.0 ([..]/root)
1431+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
1432+
",
1433+
)
1434+
.run();
1435+
1436+
p.cargo("build")
1437+
.with_stderr(
1438+
"\
1439+
[COMPILING] root v0.1.0 ([..]/root)
1440+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
1441+
",
1442+
)
1443+
.run();
1444+
1445+
t.join().ok().unwrap();
1446+
}

tests/testsuite/path.rs

+2
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ fn deep_dependencies_trigger_rebuild() {
356356
//
357357
// We base recompilation off mtime, so sleep for at least a second to ensure
358358
// that this write will change the mtime.
359+
sleep_ms(1000);
359360
File::create(&p.root().join("baz/src/baz.rs"))
360361
.unwrap()
361362
.write_all(br#"pub fn baz() { println!("hello!"); }"#)
@@ -372,6 +373,7 @@ fn deep_dependencies_trigger_rebuild() {
372373
.run();
373374

374375
// Make sure an update to bar doesn't trigger baz
376+
sleep_ms(1000);
375377
File::create(&p.root().join("bar/src/bar.rs"))
376378
.unwrap()
377379
.write_all(

0 commit comments

Comments
 (0)