Skip to content

Commit dd76122

Browse files
committed
Auto merge of #6734 - ehuss:fingerprint-build-path-only, r=alexcrichton
Fingerprint build script deps only for path packages. #6720 introduced some protection that if there is a build script, and two commands are used (such as `cargo build` then `cargo test`), the second command would correctly get rebuilt. However, the way it was implemented relies on mtimes working correctly. A common use case is to cache built dependencies in Docker, and Docker zeros the nanoseconds from mtime when the image is saved. This caused all packages that had build scripts to get rebuilt when the Docker image runs. The solution here is to only use the #6720 protection for local (path) packages. This runs under the assumption that mtimes need to work for those anyways. The consequence is that the scenario in #6720 will no longer work for detecting changes in registry dependencies with build scripts. Fixing that final edge case is nontrivial. Since it is unlikely to happen often, I figure this workaround should be sufficient for now. cc rust-lang/rust-playground#469 cc rust-lang/rust#59061
2 parents d74d879 + 5e7b50a commit dd76122

File tree

1 file changed

+40
-27
lines changed

1 file changed

+40
-27
lines changed

src/cargo/core/compiler/fingerprint.rs

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -480,36 +480,49 @@ fn calculate<'a, 'cfg>(
480480
let local = if use_dep_info(unit) {
481481
let dep_info = dep_info_loc(cx, unit);
482482
let mtime = dep_info_mtime_if_fresh(unit.pkg, &dep_info)?;
483-
LocalFingerprint::mtime(cx.files().target_root(), mtime, &dep_info)
483+
let mut local = vec![LocalFingerprint::mtime(
484+
cx.files().target_root(),
485+
mtime,
486+
&dep_info,
487+
)];
488+
// Include the fingerprint of the build script.
489+
//
490+
// This is not included for dependencies (Precalculated below) because
491+
// Docker zeros the nanosecond part of the mtime when the image is
492+
// saved, which prevents built dependencies from being cached.
493+
// This has the consequence that if a dependency needs to be rebuilt
494+
// (such as an environment variable tracked via rerun-if-env-changed),
495+
// and you run two separate commands (`build` then `test`), the second
496+
// command will erroneously think it is fresh.
497+
// See: https://github.com/rust-lang/cargo/issues/6733
498+
local.extend(
499+
cx.dep_targets(unit)
500+
.iter()
501+
.filter(|u| u.mode.is_run_custom_build())
502+
.map(|dep| {
503+
// If the build script is overridden, use the override info as
504+
// the override. Otherwise, use the last invocation time of
505+
// the build script. If the build script re-runs during this
506+
// run, dirty propagation within the JobQueue will ensure that
507+
// this gets invalidated. This is only here to catch the
508+
// situation when cargo is run a second time for another
509+
// target that wasn't built previously (such as `cargo build`
510+
// then `cargo test`).
511+
build_script_override_fingerprint(cx, unit).unwrap_or_else(|| {
512+
let ts_path = cx
513+
.files()
514+
.build_script_run_dir(dep)
515+
.join("invoked.timestamp");
516+
let ts_path_mtime = paths::mtime(&ts_path).ok();
517+
LocalFingerprint::mtime(cx.files().target_root(), ts_path_mtime, &ts_path)
518+
})
519+
}),
520+
);
521+
local
484522
} else {
485523
let fingerprint = pkg_fingerprint(&cx.bcx, unit.pkg)?;
486-
LocalFingerprint::Precalculated(fingerprint)
524+
vec![LocalFingerprint::Precalculated(fingerprint)]
487525
};
488-
let mut local = vec![local];
489-
// Include fingerprint for any build scripts this unit requires.
490-
local.extend(
491-
cx.dep_targets(unit)
492-
.iter()
493-
.filter(|u| u.mode.is_run_custom_build())
494-
.map(|dep| {
495-
// If the build script is overridden, use the override info as
496-
// the override. Otherwise, use the last invocation time of
497-
// the build script. If the build script re-runs during this
498-
// run, dirty propagation within the JobQueue will ensure that
499-
// this gets invalidated. This is only here to catch the
500-
// situation when cargo is run a second time for another
501-
// target that wasn't built previously (such as `cargo build`
502-
// then `cargo test`).
503-
build_script_override_fingerprint(cx, unit).unwrap_or_else(|| {
504-
let ts_path = cx
505-
.files()
506-
.build_script_run_dir(dep)
507-
.join("invoked.timestamp");
508-
let ts_path_mtime = paths::mtime(&ts_path).ok();
509-
LocalFingerprint::mtime(cx.files().target_root(), ts_path_mtime, &ts_path)
510-
})
511-
}),
512-
);
513526

514527
let extra_flags = if unit.mode.is_doc() {
515528
bcx.rustdocflags_args(unit)?

0 commit comments

Comments
 (0)