Skip to content

Commit b1b9b79

Browse files
committed
Track dep-info for all dependencies.
This adds dep-info tracking for non-path dependencies. This avoids tracking package-relative paths (like source files) in non-path dependencies, since we assume they are static. It also adds an mtime cache to improve performance since when rustc starts tracking sysroot files (in the future), it can cause a large number of stat calls.
1 parent aa99e9f commit b1b9b79

File tree

4 files changed

+82
-20
lines changed

4 files changed

+82
-20
lines changed

src/cargo/core/compiler/context/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::fmt::Write;
55
use std::path::PathBuf;
66
use std::sync::Arc;
77

8+
use filetime::FileTime;
89
use jobserver::Client;
910

1011
use crate::core::compiler::compilation;
@@ -34,6 +35,7 @@ pub struct Context<'a, 'cfg> {
3435
pub build_script_overridden: HashSet<(PackageId, Kind)>,
3536
pub build_explicit_deps: HashMap<Unit<'a>, BuildDeps>,
3637
pub fingerprints: HashMap<Unit<'a>, Arc<Fingerprint>>,
38+
pub mtime_cache: HashMap<PathBuf, FileTime>,
3739
pub compiled: HashSet<Unit<'a>>,
3840
pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>,
3941
pub links: Links,
@@ -82,6 +84,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
8284
compilation: Compilation::new(bcx)?,
8385
build_state: Arc::new(BuildState::new(&bcx.host_config, &bcx.target_config)),
8486
fingerprints: HashMap::new(),
87+
mtime_cache: HashMap::new(),
8588
compiled: HashSet::new(),
8689
build_scripts: HashMap::new(),
8790
build_explicit_deps: HashMap::new(),

src/cargo/core/compiler/fingerprint.rs

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
//! See the `A-rebuild-detection` flag on the issue tracker for more:
188188
//! <https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AA-rebuild-detection>
189189
190-
use std::collections::HashMap;
190+
use std::collections::hash_map::{Entry, HashMap};
191191
use std::env;
192192
use std::fs;
193193
use std::hash::{self, Hasher};
@@ -539,6 +539,7 @@ impl LocalFingerprint {
539539
/// file accesses.
540540
fn find_stale_file(
541541
&self,
542+
mtime_cache: &mut HashMap<PathBuf, FileTime>,
542543
pkg_root: &Path,
543544
target_root: &Path,
544545
) -> CargoResult<Option<StaleFile>> {
@@ -550,7 +551,7 @@ impl LocalFingerprint {
550551
LocalFingerprint::CheckDepInfo { dep_info } => {
551552
let dep_info = target_root.join(dep_info);
552553
if let Some(paths) = parse_dep_info(pkg_root, target_root, &dep_info)? {
553-
Ok(find_stale_file(&dep_info, paths.iter()))
554+
Ok(find_stale_file(mtime_cache, &dep_info, paths.iter()))
554555
} else {
555556
Ok(Some(StaleFile::Missing(dep_info)))
556557
}
@@ -559,6 +560,7 @@ impl LocalFingerprint {
559560
// We need to verify that no paths listed in `paths` are newer than
560561
// the `output` path itself, or the last time the build script ran.
561562
LocalFingerprint::RerunIfChanged { output, paths } => Ok(find_stale_file(
563+
mtime_cache,
562564
&target_root.join(output),
563565
paths.iter().map(|p| pkg_root.join(p)),
564566
)),
@@ -756,7 +758,12 @@ impl Fingerprint {
756758
/// dependencies up to this unit as well. This function assumes that the
757759
/// unit starts out as `FsStatus::Stale` and then it will optionally switch
758760
/// it to `UpToDate` if it can.
759-
fn check_filesystem(&mut self, pkg_root: &Path, target_root: &Path) -> CargoResult<()> {
761+
fn check_filesystem(
762+
&mut self,
763+
mtime_cache: &mut HashMap<PathBuf, FileTime>,
764+
pkg_root: &Path,
765+
target_root: &Path,
766+
) -> CargoResult<()> {
760767
assert!(!self.fs_status.up_to_date());
761768

762769
let mut mtimes = HashMap::new();
@@ -840,7 +847,7 @@ impl Fingerprint {
840847
// files for this package itself. If we do find something log a helpful
841848
// message and bail out so we stay stale.
842849
for local in self.local.get_mut().unwrap().iter() {
843-
if let Some(file) = local.find_stale_file(pkg_root, target_root)? {
850+
if let Some(file) = local.find_stale_file(mtime_cache, pkg_root, target_root)? {
844851
file.log();
845852
return Ok(());
846853
}
@@ -1015,7 +1022,7 @@ fn calculate<'a, 'cfg>(
10151022
// After we built the initial `Fingerprint` be sure to update the
10161023
// `fs_status` field of it.
10171024
let target_root = target_root(cx);
1018-
fingerprint.check_filesystem(unit.pkg.root(), &target_root)?;
1025+
fingerprint.check_filesystem(&mut cx.mtime_cache, unit.pkg.root(), &target_root)?;
10191026

10201027
let fingerprint = Arc::new(fingerprint);
10211028
cx.fingerprints.insert(*unit, Arc::clone(&fingerprint));
@@ -1098,13 +1105,10 @@ fn calculate_normal<'a, 'cfg>(
10981105
})
10991106
}
11001107

1101-
// We want to use the mtime for files if we're a path source, but if we're a
1102-
// git/registry source, then the mtime of files may fluctuate, but they won't
1103-
// change so long as the source itself remains constant (which is the
1104-
// responsibility of the source)
1108+
/// Whether or not the fingerprint should track the dependencies from the
1109+
/// dep-info file for this unit.
11051110
fn use_dep_info(unit: &Unit<'_>) -> bool {
1106-
let path = unit.pkg.summary().source_id().is_path();
1107-
!unit.mode.is_doc() && path
1111+
!unit.mode.is_doc()
11081112
}
11091113

11101114
/// Calculate a fingerprint for an "execute a build script" unit. This is an
@@ -1422,11 +1426,7 @@ pub fn parse_dep_info(
14221426
}
14231427
})
14241428
.collect::<Result<Vec<_>, _>>()?;
1425-
if paths.is_empty() {
1426-
Ok(None)
1427-
} else {
1428-
Ok(Some(paths))
1429-
}
1429+
Ok(Some(paths))
14301430
}
14311431

14321432
fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult<String> {
@@ -1439,7 +1439,11 @@ fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult<Str
14391439
source.fingerprint(pkg)
14401440
}
14411441

1442-
fn find_stale_file<I>(reference: &Path, paths: I) -> Option<StaleFile>
1442+
fn find_stale_file<I>(
1443+
mtime_cache: &mut HashMap<PathBuf, FileTime>,
1444+
reference: &Path,
1445+
paths: I,
1446+
) -> Option<StaleFile>
14431447
where
14441448
I: IntoIterator,
14451449
I::Item: AsRef<Path>,
@@ -1451,9 +1455,15 @@ where
14511455

14521456
for path in paths {
14531457
let path = path.as_ref();
1454-
let path_mtime = match paths::mtime(path) {
1455-
Ok(mtime) => mtime,
1456-
Err(..) => return Some(StaleFile::Missing(path.to_path_buf())),
1458+
let path_mtime = match mtime_cache.entry(path.to_path_buf()) {
1459+
Entry::Occupied(o) => *o.get(),
1460+
Entry::Vacant(v) => {
1461+
let mtime = match paths::mtime(path) {
1462+
Ok(mtime) => mtime,
1463+
Err(..) => return Some(StaleFile::Missing(path.to_path_buf())),
1464+
};
1465+
*v.insert(mtime)
1466+
}
14571467
};
14581468

14591469
// TODO: fix #5918.
@@ -1540,6 +1550,9 @@ impl DepInfoPathType {
15401550
/// The `rustc_cwd` argument is the absolute path to the cwd of the compiler
15411551
/// when it was invoked.
15421552
///
1553+
/// If the `allow_package` argument is false, then package-relative paths are
1554+
/// skipped and ignored.
1555+
///
15431556
/// The serialized Cargo format will contain a list of files, all of which are
15441557
/// relative if they're under `root`. or absolute if they're elsewhere.
15451558
pub fn translate_dep_info(
@@ -1548,6 +1561,7 @@ pub fn translate_dep_info(
15481561
rustc_cwd: &Path,
15491562
pkg_root: &Path,
15501563
target_root: &Path,
1564+
allow_package: bool,
15511565
) -> CargoResult<()> {
15521566
let target = parse_rustc_dep_info(rustc_dep_info)?;
15531567
let deps = &target
@@ -1561,6 +1575,9 @@ pub fn translate_dep_info(
15611575
let (ty, path) = if let Ok(stripped) = file.strip_prefix(target_root) {
15621576
(DepInfoPathType::TargetRootRelative, stripped)
15631577
} else if let Ok(stripped) = file.strip_prefix(pkg_root) {
1578+
if !allow_package {
1579+
continue;
1580+
}
15641581
(DepInfoPathType::PackageRootRelative, stripped)
15651582
} else {
15661583
// It's definitely not target root relative, but this is an absolute path (since it was

src/cargo/core/compiler/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ fn rustc<'a, 'cfg>(
319319
&cwd,
320320
&pkg_root,
321321
&target_dir,
322+
// Do not track source files in the fingerprint for registry dependencies.
323+
current_id.source_id().is_path(),
322324
)
323325
.chain_err(|| {
324326
internal(format!(

tests/testsuite/dep_info.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,3 +420,43 @@ fn relative_depinfo_paths_no_ws() {
420420
// Make sure it stays fresh.
421421
p.cargo("build").with_stderr("[FINISHED] dev [..]").run();
422422
}
423+
424+
#[cargo_test]
425+
fn reg_dep_source_not_tracked() {
426+
// Make sure source files in dep-info file are not tracked for registry dependencies.
427+
Package::new("regdep", "0.1.0")
428+
.file("src/lib.rs", "pub fn f() {}")
429+
.publish();
430+
431+
let p = project()
432+
.file(
433+
"Cargo.toml",
434+
r#"
435+
[package]
436+
name = "foo"
437+
version = "0.1.0"
438+
439+
[dependencies]
440+
regdep = "0.1"
441+
"#,
442+
)
443+
.file("src/lib.rs", "pub fn f() { regdep::f(); }")
444+
.build();
445+
446+
p.cargo("build").run();
447+
448+
assert_deps(
449+
&p,
450+
"target/debug/.fingerprint/regdep-*/dep-lib-regdep-*",
451+
|info_path, entries| {
452+
for (kind, path) in entries {
453+
if *kind == 1 {
454+
panic!(
455+
"Did not expect package root relative path type: {:?} in {:?}",
456+
path, info_path
457+
);
458+
}
459+
}
460+
},
461+
);
462+
}

0 commit comments

Comments
 (0)