187
187
//! See the `A-rebuild-detection` flag on the issue tracker for more:
188
188
//! <https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AA-rebuild-detection>
189
189
190
- use std:: collections:: HashMap ;
190
+ use std:: collections:: hash_map :: { Entry , HashMap } ;
191
191
use std:: env;
192
192
use std:: fs;
193
193
use std:: hash:: { self , Hasher } ;
@@ -213,7 +213,7 @@ use super::job::{
213
213
Freshness :: { Dirty , Fresh } ,
214
214
Job , Work ,
215
215
} ;
216
- use super :: { BuildContext , Context , FileFlavor , Kind , Unit } ;
216
+ use super :: { BuildContext , Context , FileFlavor , Unit } ;
217
217
218
218
/// Determines if a `unit` is up-to-date, and if not prepares necessary work to
219
219
/// update the persisted fingerprint.
@@ -539,6 +539,7 @@ impl LocalFingerprint {
539
539
/// file accesses.
540
540
fn find_stale_file (
541
541
& self ,
542
+ mtime_cache : & mut HashMap < PathBuf , FileTime > ,
542
543
pkg_root : & Path ,
543
544
target_root : & Path ,
544
545
) -> CargoResult < Option < StaleFile > > {
@@ -550,7 +551,7 @@ impl LocalFingerprint {
550
551
LocalFingerprint :: CheckDepInfo { dep_info } => {
551
552
let dep_info = target_root. join ( dep_info) ;
552
553
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 ( ) ) )
554
555
} else {
555
556
Ok ( Some ( StaleFile :: Missing ( dep_info) ) )
556
557
}
@@ -559,6 +560,7 @@ impl LocalFingerprint {
559
560
// We need to verify that no paths listed in `paths` are newer than
560
561
// the `output` path itself, or the last time the build script ran.
561
562
LocalFingerprint :: RerunIfChanged { output, paths } => Ok ( find_stale_file (
563
+ mtime_cache,
562
564
& target_root. join ( output) ,
563
565
paths. iter ( ) . map ( |p| pkg_root. join ( p) ) ,
564
566
) ) ,
@@ -756,7 +758,12 @@ impl Fingerprint {
756
758
/// dependencies up to this unit as well. This function assumes that the
757
759
/// unit starts out as `FsStatus::Stale` and then it will optionally switch
758
760
/// 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 < ( ) > {
760
767
assert ! ( !self . fs_status. up_to_date( ) ) ;
761
768
762
769
let mut mtimes = HashMap :: new ( ) ;
@@ -840,7 +847,7 @@ impl Fingerprint {
840
847
// files for this package itself. If we do find something log a helpful
841
848
// message and bail out so we stay stale.
842
849
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) ? {
844
851
file. log ( ) ;
845
852
return Ok ( ( ) ) ;
846
853
}
@@ -1014,8 +1021,8 @@ fn calculate<'a, 'cfg>(
1014
1021
1015
1022
// After we built the initial `Fingerprint` be sure to update the
1016
1023
// `fs_status` field of it.
1017
- let target_root = target_root ( cx, unit ) ;
1018
- fingerprint. check_filesystem ( unit. pkg . root ( ) , & target_root) ?;
1024
+ let target_root = target_root ( cx) ;
1025
+ fingerprint. check_filesystem ( & mut cx . mtime_cache , unit. pkg . root ( ) , & target_root) ?;
1019
1026
1020
1027
let fingerprint = Arc :: new ( fingerprint) ;
1021
1028
cx. fingerprints . insert ( * unit, Arc :: clone ( & fingerprint) ) ;
@@ -1046,7 +1053,7 @@ fn calculate_normal<'a, 'cfg>(
1046
1053
// correctly, but otherwise upstream packages like from crates.io or git
1047
1054
// get bland fingerprints because they don't change without their
1048
1055
// `PackageId` changing.
1049
- let target_root = target_root ( cx, unit ) ;
1056
+ let target_root = target_root ( cx) ;
1050
1057
let local = if use_dep_info ( unit) {
1051
1058
let dep_info = dep_info_loc ( cx, unit) ;
1052
1059
let dep_info = dep_info. strip_prefix ( & target_root) . unwrap ( ) . to_path_buf ( ) ;
@@ -1098,13 +1105,10 @@ fn calculate_normal<'a, 'cfg>(
1098
1105
} )
1099
1106
}
1100
1107
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.
1105
1110
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 ( )
1108
1112
}
1109
1113
1110
1114
/// Calculate a fingerprint for an "execute a build script" unit. This is an
@@ -1219,8 +1223,8 @@ fn build_script_local_fingerprints<'a, 'cfg>(
1219
1223
// package. Remember that the fact that this is an `Option` is a bug, but a
1220
1224
// longstanding bug, in Cargo. Recent refactorings just made it painfully
1221
1225
// obvious.
1222
- let script_root = cx. files ( ) . build_script_run_dir ( unit) ;
1223
1226
let pkg_root = unit. pkg . root ( ) . to_path_buf ( ) ;
1227
+ let target_dir = target_root ( cx) ;
1224
1228
let calculate =
1225
1229
move |deps : & BuildDeps , pkg_fingerprint : Option < & dyn Fn ( ) -> CargoResult < String > > | {
1226
1230
if deps. rerun_if_changed . is_empty ( ) && deps. rerun_if_env_changed . is_empty ( ) {
@@ -1247,7 +1251,7 @@ fn build_script_local_fingerprints<'a, 'cfg>(
1247
1251
// Ok so now we're in "new mode" where we can have files listed as
1248
1252
// dependencies as well as env vars listed as dependencies. Process
1249
1253
// them all here.
1250
- Ok ( Some ( local_fingerprints_deps ( deps, & script_root , & pkg_root) ) )
1254
+ Ok ( Some ( local_fingerprints_deps ( deps, & target_dir , & pkg_root) ) )
1251
1255
} ;
1252
1256
1253
1257
// Note that `false` == "not overridden"
@@ -1346,17 +1350,10 @@ pub fn dep_info_loc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> Pa
1346
1350
. join ( & format ! ( "dep-{}" , filename( cx, unit) ) )
1347
1351
}
1348
1352
1349
- /// Returns an absolute path that the `unit`'s outputs should always be relative
1350
- /// to. This `target_root` variable is used to store relative path names in
1351
- /// `Fingerprint` instead of absolute pathnames (see module comment).
1352
- fn target_root < ' a , ' cfg > ( cx : & mut Context < ' a , ' cfg > , unit : & Unit < ' a > ) -> PathBuf {
1353
- if unit. mode . is_run_custom_build ( ) {
1354
- cx. files ( ) . build_script_run_dir ( unit)
1355
- } else if unit. kind == Kind :: Host {
1356
- cx. files ( ) . host_root ( ) . to_path_buf ( )
1357
- } else {
1358
- cx. files ( ) . target_root ( ) . to_path_buf ( )
1359
- }
1353
+ /// Returns an absolute path that target directory.
1354
+ /// All paths are rewritten to be relative to this.
1355
+ fn target_root ( cx : & Context < ' _ , ' _ > ) -> PathBuf {
1356
+ cx. bcx . ws . target_dir ( ) . into_path_unlocked ( )
1360
1357
}
1361
1358
1362
1359
fn compare_old_fingerprint (
@@ -1429,11 +1426,7 @@ pub fn parse_dep_info(
1429
1426
}
1430
1427
} )
1431
1428
. collect :: < Result < Vec < _ > , _ > > ( ) ?;
1432
- if paths. is_empty ( ) {
1433
- Ok ( None )
1434
- } else {
1435
- Ok ( Some ( paths) )
1436
- }
1429
+ Ok ( Some ( paths) )
1437
1430
}
1438
1431
1439
1432
fn pkg_fingerprint ( bcx : & BuildContext < ' _ , ' _ > , pkg : & Package ) -> CargoResult < String > {
@@ -1446,7 +1439,11 @@ fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult<Str
1446
1439
source. fingerprint ( pkg)
1447
1440
}
1448
1441
1449
- 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 >
1450
1447
where
1451
1448
I : IntoIterator ,
1452
1449
I :: Item : AsRef < Path > ,
@@ -1458,9 +1455,15 @@ where
1458
1455
1459
1456
for path in paths {
1460
1457
let path = path. as_ref ( ) ;
1461
- let path_mtime = match paths:: mtime ( path) {
1462
- Ok ( mtime) => mtime,
1463
- 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
+ }
1464
1467
} ;
1465
1468
1466
1469
// TODO: fix #5918.
@@ -1547,6 +1550,12 @@ impl DepInfoPathType {
1547
1550
/// The `rustc_cwd` argument is the absolute path to the cwd of the compiler
1548
1551
/// when it was invoked.
1549
1552
///
1553
+ /// If the `allow_package` argument is true, then package-relative paths are
1554
+ /// included. If it is false, then package-relative paths are skipped and
1555
+ /// ignored (typically used for registry or git dependencies where we assume
1556
+ /// the source never changes, and we don't want the cost of running `stat` on
1557
+ /// all those files).
1558
+ ///
1550
1559
/// The serialized Cargo format will contain a list of files, all of which are
1551
1560
/// relative if they're under `root`. or absolute if they're elsewhere.
1552
1561
pub fn translate_dep_info (
@@ -1555,26 +1564,35 @@ pub fn translate_dep_info(
1555
1564
rustc_cwd : & Path ,
1556
1565
pkg_root : & Path ,
1557
1566
target_root : & Path ,
1567
+ allow_package : bool ,
1558
1568
) -> CargoResult < ( ) > {
1559
1569
let target = parse_rustc_dep_info ( rustc_dep_info) ?;
1560
1570
let deps = & target
1561
1571
. get ( 0 )
1562
1572
. ok_or_else ( || internal ( "malformed dep-info format, no targets" . to_string ( ) ) ) ?
1563
1573
. 1 ;
1564
1574
1575
+ let target_root = target_root. canonicalize ( ) ?;
1576
+ let pkg_root = pkg_root. canonicalize ( ) ?;
1565
1577
let mut new_contents = Vec :: new ( ) ;
1566
1578
for file in deps {
1567
- let file = rustc_cwd. join ( file) ;
1568
- let ( ty, path) = if let Ok ( stripped) = file. strip_prefix ( pkg_root) {
1569
- ( DepInfoPathType :: PackageRootRelative , stripped)
1570
- } else if let Ok ( stripped) = file. strip_prefix ( target_root) {
1579
+ // The path may be absolute or relative, canonical or not. Make sure
1580
+ // it is canonicalized so we are comparing the same kinds of paths.
1581
+ let canon_file = rustc_cwd. join ( file) . canonicalize ( ) ?;
1582
+ let abs_file = rustc_cwd. join ( file) ;
1583
+
1584
+ let ( ty, path) = if let Ok ( stripped) = canon_file. strip_prefix ( & target_root) {
1571
1585
( DepInfoPathType :: TargetRootRelative , stripped)
1586
+ } else if let Ok ( stripped) = canon_file. strip_prefix ( & pkg_root) {
1587
+ if !allow_package {
1588
+ continue ;
1589
+ }
1590
+ ( DepInfoPathType :: PackageRootRelative , stripped)
1572
1591
} else {
1573
1592
// It's definitely not target root relative, but this is an absolute path (since it was
1574
1593
// joined to rustc_cwd) and as such re-joining it later to the target root will have no
1575
1594
// effect.
1576
- assert ! ( file. is_absolute( ) , "{:?} is absolute" , file) ;
1577
- ( DepInfoPathType :: TargetRootRelative , & * file)
1595
+ ( DepInfoPathType :: TargetRootRelative , & * abs_file)
1578
1596
} ;
1579
1597
new_contents. push ( ty as u8 ) ;
1580
1598
new_contents. extend ( util:: path2bytes ( path) ?) ;
0 commit comments