cargo/core/compiler/fingerprint/
mod.rs

1//! Tracks changes to determine if something needs to be recompiled.
2//!
3//! This module implements change-tracking so that Cargo can know whether or
4//! not something needs to be recompiled. A Cargo [`Unit`] can be either "dirty"
5//! (needs to be recompiled) or "fresh" (it does not need to be recompiled).
6//!
7//! ## Mechanisms affecting freshness
8//!
9//! There are several mechanisms that influence a Unit's freshness:
10//!
11//! - The [`Fingerprint`] is a hash, saved to the filesystem in the
12//!   `.fingerprint` directory, that tracks information about the Unit. If the
13//!   fingerprint is missing (such as the first time the unit is being
14//!   compiled), then the unit is dirty. If any of the fingerprint fields
15//!   change (like the name of the source file), then the Unit is considered
16//!   dirty.
17//!
18//!   The `Fingerprint` also tracks the fingerprints of all its dependencies,
19//!   so a change in a dependency will propagate the "dirty" status up.
20//!
21//! - Filesystem mtime tracking is also used to check if a unit is dirty.
22//!   See the section below on "Mtime comparison" for more details. There
23//!   are essentially two parts to mtime tracking:
24//!
25//!   1. The mtime of a Unit's output files is compared to the mtime of all
26//!      its dependencies' output file mtimes (see
27//!      [`check_filesystem`]). If any output is missing, or is
28//!      older than a dependency's output, then the unit is dirty.
29//!   2. The mtime of a Unit's source files is compared to the mtime of its
30//!      dep-info file in the fingerprint directory (see [`find_stale_file`]).
31//!      The dep-info file is used as an anchor to know when the last build of
32//!      the unit was done. See the "dep-info files" section below for more
33//!      details. If any input files are missing, or are newer than the
34//!      dep-info, then the unit is dirty.
35//!
36//!  - Alternatively if you're using the unstable feature `checksum-freshness`
37//!    mtimes are ignored entirely in favor of comparing first the file size, and
38//!    then the checksum with a known prior value emitted by rustc. Only nightly
39//!    rustc will emit the needed metadata at the time of writing. This is dependent
40//!    on the unstable feature `-Z checksum-hash-algorithm`.
41//!
42//! Note: Fingerprinting is not a perfect solution. Filesystem mtime tracking
43//! is notoriously imprecise and problematic. Only a small part of the
44//! environment is captured. This is a balance of performance, simplicity, and
45//! completeness. Sandboxing, hashing file contents, tracking every file
46//! access, environment variable, and network operation would ensure more
47//! reliable and reproducible builds at the cost of being complex, slow, and
48//! platform-dependent.
49//!
50//! ## Fingerprints and [`UnitHash`]s
51//!
52//! [`Metadata`] tracks several [`UnitHash`]s, including
53//! [`Metadata::unit_id`], [`Metadata::c_metadata`], and [`Metadata::c_extra_filename`].
54//! See its documentation for more details.
55//!
56//! NOTE: Not all output files are isolated via filename hashes (like dylibs).
57//! The fingerprint directory uses a hash, but sometimes units share the same
58//! fingerprint directory (when they don't have Metadata) so care should be
59//! taken to handle this!
60//!
61//! Fingerprints and [`UnitHash`]s are similar, and track some of the same things.
62//! [`UnitHash`]s contains information that is required to keep Units separate.
63//! The Fingerprint includes additional information that should cause a
64//! recompile, but it is desired to reuse the same filenames. A comparison
65//! of what is tracked:
66//!
67//! Value                                      | Fingerprint | `Metadata::unit_id` | `Metadata::c_metadata` | `Metadata::c_extra_filename`
68//! -------------------------------------------|-------------|---------------------|------------------------|----------
69//! rustc                                      | ✓           | ✓                   | ✓                      | ✓
70//! [`Profile`]                                | ✓           | ✓                   | ✓                      | ✓
71//! `cargo rustc` extra args                   | ✓           | ✓[^7]               |                        | ✓[^7]
72//! [`CompileMode`]                            | ✓           | ✓                   | ✓                      | ✓
73//! Target Name                                | ✓           | ✓                   | ✓                      | ✓
74//! `TargetKind` (bin/lib/etc.)                | ✓           | ✓                   | ✓                      | ✓
75//! Enabled Features                           | ✓           | ✓                   | ✓                      | ✓
76//! Declared Features                          | ✓           |                     |                        |
77//! Immediate dependency’s hashes              | ✓[^1]       | ✓                   | ✓                      | ✓
78//! [`CompileKind`] (host/target)              | ✓           | ✓                   | ✓                      | ✓
79//! `__CARGO_DEFAULT_LIB_METADATA`[^4]         |             | ✓                   | ✓                      | ✓
80//! `package_id`                               |             | ✓                   | ✓                      | ✓
81//! Target src path relative to ws             | ✓           |                     |                        |
82//! Target flags (test/bench/for_host/edition) | ✓           |                     |                        |
83//! -C incremental=… flag                      | ✓           |                     |                        |
84//! mtime of sources                           | ✓[^3]       |                     |                        |
85//! RUSTFLAGS/RUSTDOCFLAGS                     | ✓           | ✓[^7]               |                        | ✓[^7]
86//! [`Lto`] flags                              | ✓           | ✓                   | ✓                      | ✓
87//! config settings[^5]                        | ✓           |                     |                        |
88//! `is_std`                                   |             | ✓                   | ✓                      | ✓
89//! `[lints]` table[^6]                        | ✓           |                     |                        |
90//! `[lints.rust.unexpected_cfgs.check-cfg]`   | ✓           |                     |                        |
91//!
92//! [^1]: Build script and bin dependencies are not included.
93//!
94//! [^3]: See below for details on mtime tracking.
95//!
96//! [^4]: `__CARGO_DEFAULT_LIB_METADATA` is set by rustbuild to embed the
97//!        release channel (bootstrap/stable/beta/nightly) in libstd.
98//!
99//! [^5]: Config settings that are not otherwise captured anywhere else.
100//!       Currently, this is only `doc.extern-map`.
101//!
102//! [^6]: Via [`Manifest::lint_rustflags`][crate::core::Manifest::lint_rustflags]
103//!
104//! [^7]: extra-flags and RUSTFLAGS are conditionally excluded when `--remap-path-prefix` is
105//!       present to avoid breaking build reproducibility while we wait for trim-paths
106//!
107//! When deciding what should go in the Metadata vs the Fingerprint, consider
108//! that some files (like dylibs) do not have a hash in their filename. Thus,
109//! if a value changes, only the fingerprint will detect the change (consider,
110//! for example, swapping between different features). Fields that are only in
111//! Metadata generally aren't relevant to the fingerprint because they
112//! fundamentally change the output (like target vs host changes the directory
113//! where it is emitted).
114//!
115//! ## Fingerprint files
116//!
117//! Fingerprint information is stored in the
118//! `target/{debug,release}/.fingerprint/` directory. Each Unit is stored in a
119//! separate directory. Each Unit directory contains:
120//!
121//! - A file with a 16 hex-digit hash. This is the Fingerprint hash, used for
122//!   quick loading and comparison.
123//! - A `.json` file that contains details about the Fingerprint. This is only
124//!   used to log details about *why* a fingerprint is considered dirty.
125//!   `CARGO_LOG=cargo::core::compiler::fingerprint=trace cargo build` can be
126//!   used to display this log information.
127//! - A "dep-info" file which is a translation of rustc's `*.d` dep-info files
128//!   to a Cargo-specific format that tweaks file names and is optimized for
129//!   reading quickly.
130//! - An `invoked.timestamp` file whose filesystem mtime is updated every time
131//!   the Unit is built. This is used for capturing the time when the build
132//!   starts, to detect if files are changed in the middle of the build. See
133//!   below for more details.
134//!
135//! Note that some units are a little different. A Unit for *running* a build
136//! script or for `rustdoc` does not have a dep-info file (it's not
137//! applicable). Build script `invoked.timestamp` files are in the build
138//! output directory.
139//!
140//! ## Fingerprint calculation
141//!
142//! After the list of Units has been calculated, the Units are added to the
143//! [`JobQueue`]. As each one is added, the fingerprint is calculated, and the
144//! dirty/fresh status is recorded. A closure is used to update the fingerprint
145//! on-disk when the Unit successfully finishes. The closure will recompute the
146//! Fingerprint based on the updated information. If the Unit fails to compile,
147//! the fingerprint is not updated.
148//!
149//! Fingerprints are cached in the [`BuildRunner`]. This makes computing
150//! Fingerprints faster, but also is necessary for properly updating
151//! dependency information. Since a Fingerprint includes the Fingerprints of
152//! all dependencies, when it is updated, by using `Arc` clones, it
153//! automatically picks up the updates to its dependencies.
154//!
155//! ### dep-info files
156//!
157//! Cargo has several kinds of "dep info" files:
158//!
159//! * dep-info files generated by `rustc`.
160//! * Fingerprint dep-info files translated from the first one.
161//! * dep-info for external build system integration.
162//! * Unstable `-Zbinary-dep-depinfo`.
163//!
164//! #### `rustc` dep-info files
165//!
166//! Cargo passes the `--emit=dep-info` flag to `rustc` so that `rustc` will
167//! generate a "dep info" file (with the `.d` extension). This is a
168//! Makefile-like syntax that includes all of the source files used to build
169//! the crate. This file is used by Cargo to know which files to check to see
170//! if the crate will need to be rebuilt. Example:
171//!
172//! ```makefile
173//! /path/to/target/debug/deps/cargo-b6219d178925203d: src/bin/main.rs src/bin/cargo/cli.rs # … etc.
174//! ```
175//!
176//! #### Fingerprint dep-info files
177//!
178//! After `rustc` exits successfully, Cargo will read the first kind of dep
179//! info file and translate it into a binary format that is stored in the
180//! fingerprint directory ([`translate_dep_info`]).
181//!
182//! These are used to quickly scan for any changed files. The mtime of the
183//! fingerprint dep-info file itself is used as the reference for comparing the
184//! source files to determine if any of the source files have been modified
185//! (see [below](#mtime-comparison) for more detail).
186//!
187//! Note that Cargo parses the special `# env-var:...` comments in dep-info
188//! files to learn about environment variables that the rustc compile depends on.
189//! Cargo then later uses this to trigger a recompile if a referenced env var
190//! changes (even if the source didn't change).
191//! This also includes env vars generated from Cargo metadata like `CARGO_PKG_DESCRIPTION`.
192//! (See [`crate::core::manifest::ManifestMetadata`]
193//!
194//! #### dep-info files for build system integration.
195//!
196//! There is also a third dep-info file. Cargo will extend the file created by
197//! rustc with some additional information and saves this into the output
198//! directory. This is intended for build system integration. See the
199//! [`output_depinfo`] function for more detail.
200//!
201//! #### -Zbinary-dep-depinfo
202//!
203//! `rustc` has an experimental flag `-Zbinary-dep-depinfo`. This causes
204//! `rustc` to include binary files (like rlibs) in the dep-info file. This is
205//! primarily to support rustc development, so that Cargo can check the
206//! implicit dependency to the standard library (which lives in the sysroot).
207//! We want Cargo to recompile whenever the standard library rlib/dylibs
208//! change, and this is a generic mechanism to make that work.
209//!
210//! ### Mtime comparison
211//!
212//! The use of modification timestamps is the most common way a unit will be
213//! determined to be dirty or fresh between builds. There are many subtle
214//! issues and edge cases with mtime comparisons. This gives a high-level
215//! overview, but you'll need to read the code for the gritty details. Mtime
216//! handling is different for different unit kinds. The different styles are
217//! driven by the [`Fingerprint::local`] field, which is set based on the unit
218//! kind.
219//!
220//! The status of whether or not the mtime is "stale" or "up-to-date" is
221//! stored in [`Fingerprint::fs_status`].
222//!
223//! All units will compare the mtime of its newest output file with the mtimes
224//! of the outputs of all its dependencies. If any output file is missing,
225//! then the unit is stale. If any dependency is newer, the unit is stale.
226//!
227//! #### Normal package mtime handling
228//!
229//! [`LocalFingerprint::CheckDepInfo`] is used for checking the mtime of
230//! packages. It compares the mtime of the input files (the source files) to
231//! the mtime of the dep-info file (which is written last after a build is
232//! finished). If the dep-info is missing, the unit is stale (it has never
233//! been built). The list of input files comes from the dep-info file. See the
234//! section above for details on dep-info files.
235//!
236//! Also note that although registry and git packages use [`CheckDepInfo`], none
237//! of their source files are included in the dep-info (see
238//! [`translate_dep_info`]), so for those kinds no mtime checking is done
239//! (unless `-Zbinary-dep-depinfo` is used). Repository and git packages are
240//! static, so there is no need to check anything.
241//!
242//! When a build is complete, the mtime of the dep-info file in the
243//! fingerprint directory is modified to rewind it to the time when the build
244//! started. This is done by creating an `invoked.timestamp` file when the
245//! build starts to capture the start time. The mtime is rewound to the start
246//! to handle the case where the user modifies a source file while a build is
247//! running. Cargo can't know whether or not the file was included in the
248//! build, so it takes a conservative approach of assuming the file was *not*
249//! included, and it should be rebuilt during the next build.
250//!
251//! #### Rustdoc mtime handling
252//!
253//! Rustdoc does not emit a dep-info file, so Cargo currently has a relatively
254//! simple system for detecting rebuilds. [`LocalFingerprint::Precalculated`] is
255//! used for rustdoc units. For registry packages, this is the package
256//! version. For git packages, it is the git hash. For path packages, it is
257//! the a string of the mtime of the newest file in the package.
258//!
259//! There are some known bugs with how this works, so it should be improved at
260//! some point.
261//!
262//! #### Build script mtime handling
263//!
264//! Build script mtime handling runs in different modes. There is the "old
265//! style" where the build script does not emit any `rerun-if` directives. In
266//! this mode, Cargo will use [`LocalFingerprint::Precalculated`]. See the
267//! "rustdoc" section above how it works.
268//!
269//! In the new-style, each `rerun-if` directive is translated to the
270//! corresponding [`LocalFingerprint`] variant. The [`RerunIfChanged`] variant
271//! compares the mtime of the given filenames against the mtime of the
272//! "output" file.
273//!
274//! Similar to normal units, the build script "output" file mtime is rewound
275//! to the time just before the build script is executed to handle mid-build
276//! modifications.
277//!
278//! ## Considerations for inclusion in a fingerprint
279//!
280//! Over time we've realized a few items which historically were included in
281//! fingerprint hashings should not actually be included. Examples are:
282//!
283//! * Modification time values. We strive to never include a modification time
284//!   inside a `Fingerprint` to get hashed into an actual value. While
285//!   theoretically fine to do, in practice this causes issues with common
286//!   applications like Docker. Docker, after a layer is built, will zero out
287//!   the nanosecond part of all filesystem modification times. This means that
288//!   the actual modification time is different for all build artifacts, which
289//!   if we tracked the actual values of modification times would cause
290//!   unnecessary recompiles. To fix this we instead only track paths which are
291//!   relevant. These paths are checked dynamically to see if they're up to
292//!   date, and the modification time doesn't make its way into the fingerprint
293//!   hash.
294//!
295//! * Absolute path names. We strive to maintain a property where if you rename
296//!   a project directory Cargo will continue to preserve all build artifacts
297//!   and reuse the cache. This means that we can't ever hash an absolute path
298//!   name. Instead we always hash relative path names and the "root" is passed
299//!   in at runtime dynamically. Some of this is best effort, but the general
300//!   idea is that we assume all accesses within a crate stay within that
301//!   crate.
302//!
303//! These are pretty tricky to test for unfortunately, but we should have a good
304//! test suite nowadays and lord knows Cargo gets enough testing in the wild!
305//!
306//! ## Build scripts
307//!
308//! The *running* of a build script ([`CompileMode::RunCustomBuild`]) is treated
309//! significantly different than all other Unit kinds. It has its own function
310//! for calculating the Fingerprint ([`calculate_run_custom_build`]) and has some
311//! unique considerations. It does not track the same information as a normal
312//! Unit. The information tracked depends on the `rerun-if-changed` and
313//! `rerun-if-env-changed` statements produced by the build script. If the
314//! script does not emit either of these statements, the Fingerprint runs in
315//! "old style" mode where an mtime change of *any* file in the package will
316//! cause the build script to be re-run. Otherwise, the fingerprint *only*
317//! tracks the individual "rerun-if" items listed by the build script.
318//!
319//! The "rerun-if" statements from a *previous* build are stored in the build
320//! output directory in a file called `output`. Cargo parses this file when
321//! the Unit for that build script is prepared for the [`JobQueue`]. The
322//! Fingerprint code can then use that information to compute the Fingerprint
323//! and compare against the old fingerprint hash.
324//!
325//! Care must be taken with build script Fingerprints because the
326//! [`Fingerprint::local`] value may be changed after the build script runs
327//! (such as if the build script adds or removes "rerun-if" items).
328//!
329//! Another complication is if a build script is overridden. In that case, the
330//! fingerprint is the hash of the output of the override.
331//!
332//! ## Special considerations
333//!
334//! Registry dependencies do not track the mtime of files. This is because
335//! registry dependencies are not expected to change (if a new version is
336//! used, the Package ID will change, causing a rebuild). Cargo currently
337//! partially works with Docker caching. When a Docker image is built, it has
338//! normal mtime information. However, when a step is cached, the nanosecond
339//! portions of all files is zeroed out. Currently this works, but care must
340//! be taken for situations like these.
341//!
342//! HFS on macOS only supports 1 second timestamps. This causes a significant
343//! number of problems, particularly with Cargo's testsuite which does rapid
344//! builds in succession. Other filesystems have various degrees of
345//! resolution.
346//!
347//! Various weird filesystems (such as network filesystems) also can cause
348//! complications. Network filesystems may track the time on the server
349//! (except when the time is set manually such as with
350//! `filetime::set_file_times`). Not all filesystems support modifying the
351//! mtime.
352//!
353//! See the [`A-rebuild-detection`] label on the issue tracker for more.
354//!
355//! [`check_filesystem`]: Fingerprint::check_filesystem
356//! [`Metadata`]: crate::core::compiler::Metadata
357//! [`Metadata::unit_id`]: crate::core::compiler::Metadata::unit_id
358//! [`Metadata::c_metadata`]: crate::core::compiler::Metadata::c_metadata
359//! [`Metadata::c_extra_filename`]: crate::core::compiler::Metadata::c_extra_filename
360//! [`UnitHash`]: crate::core::compiler::UnitHash
361//! [`Profile`]: crate::core::profiles::Profile
362//! [`CompileMode`]: crate::core::compiler::CompileMode
363//! [`Lto`]: crate::core::compiler::Lto
364//! [`CompileKind`]: crate::core::compiler::CompileKind
365//! [`JobQueue`]: super::job_queue::JobQueue
366//! [`output_depinfo`]: super::output_depinfo()
367//! [`CheckDepInfo`]: LocalFingerprint::CheckDepInfo
368//! [`RerunIfChanged`]: LocalFingerprint::RerunIfChanged
369//! [`CompileMode::RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild
370//! [`A-rebuild-detection`]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AA-rebuild-detection
371
372mod dep_info;
373mod dirty_reason;
374
375use std::collections::hash_map::{Entry, HashMap};
376use std::env;
377use std::ffi::OsString;
378use std::fs;
379use std::fs::File;
380use std::hash::{self, Hash, Hasher};
381use std::io::{self};
382use std::path::{Path, PathBuf};
383use std::sync::{Arc, Mutex};
384use std::time::SystemTime;
385
386use anyhow::format_err;
387use anyhow::Context as _;
388use cargo_util::paths;
389use filetime::FileTime;
390use serde::de;
391use serde::ser;
392use serde::{Deserialize, Serialize};
393use tracing::{debug, info};
394
395use crate::core::compiler::unit_graph::UnitDep;
396use crate::core::Package;
397use crate::util;
398use crate::util::errors::CargoResult;
399use crate::util::interning::InternedString;
400use crate::util::{internal, path_args, StableHasher};
401use crate::{GlobalContext, CARGO_ENV};
402
403use super::custom_build::BuildDeps;
404use super::{BuildContext, BuildRunner, FileFlavor, Job, Unit, Work};
405
406pub use self::dep_info::parse_dep_info;
407pub use self::dep_info::parse_rustc_dep_info;
408pub use self::dep_info::translate_dep_info;
409pub use self::dep_info::Checksum;
410pub use self::dirty_reason::DirtyReason;
411
412/// Determines if a [`Unit`] is up-to-date, and if not prepares necessary work to
413/// update the persisted fingerprint.
414///
415/// This function will inspect `Unit`, calculate a fingerprint for it, and then
416/// return an appropriate [`Job`] to run. The returned `Job` will be a noop if
417/// `unit` is considered "fresh", or if it was previously built and cached.
418/// Otherwise the `Job` returned will write out the true fingerprint to the
419/// filesystem, to be executed after the unit's work has completed.
420///
421/// The `force` flag is a way to force the `Job` to be "dirty", or always
422/// update the fingerprint. **Beware using this flag** because it does not
423/// transitively propagate throughout the dependency graph, it only forces this
424/// one unit which is very unlikely to be what you want unless you're
425/// exclusively talking about top-level units.
426#[tracing::instrument(
427    skip(build_runner, unit),
428    fields(package_id = %unit.pkg.package_id(), target = unit.target.name())
429)]
430pub fn prepare_target(
431    build_runner: &mut BuildRunner<'_, '_>,
432    unit: &Unit,
433    force: bool,
434) -> CargoResult<Job> {
435    let bcx = build_runner.bcx;
436    let loc = build_runner.files().fingerprint_file_path(unit, "");
437
438    debug!("fingerprint at: {}", loc.display());
439
440    // Figure out if this unit is up to date. After calculating the fingerprint
441    // compare it to an old version, if any, and attempt to print diagnostic
442    // information about failed comparisons to aid in debugging.
443    let fingerprint = calculate(build_runner, unit)?;
444    let mtime_on_use = build_runner.bcx.gctx.cli_unstable().mtime_on_use;
445    let dirty_reason = compare_old_fingerprint(unit, &loc, &*fingerprint, mtime_on_use, force);
446
447    let Some(dirty_reason) = dirty_reason else {
448        return Ok(Job::new_fresh());
449    };
450
451    // We're going to rebuild, so ensure the source of the crate passes all
452    // verification checks before we build it.
453    //
454    // The `Source::verify` method is intended to allow sources to execute
455    // pre-build checks to ensure that the relevant source code is all
456    // up-to-date and as expected. This is currently used primarily for
457    // directory sources which will use this hook to perform an integrity check
458    // on all files in the source to ensure they haven't changed. If they have
459    // changed then an error is issued.
460    let source_id = unit.pkg.package_id().source_id();
461    let sources = bcx.packages.sources();
462    let source = sources
463        .get(source_id)
464        .ok_or_else(|| internal("missing package source"))?;
465    source.verify(unit.pkg.package_id())?;
466
467    // Clear out the old fingerprint file if it exists. This protects when
468    // compilation is interrupted leaving a corrupt file. For example, a
469    // project with a lib.rs and integration test (two units):
470    //
471    // 1. Build the library and integration test.
472    // 2. Make a change to lib.rs (NOT the integration test).
473    // 3. Build the integration test, hit Ctrl-C while linking. With gcc, this
474    //    will leave behind an incomplete executable (zero size, or partially
475    //    written). NOTE: The library builds successfully, it is the linking
476    //    of the integration test that we are interrupting.
477    // 4. Build the integration test again.
478    //
479    // Without the following line, then step 3 will leave a valid fingerprint
480    // on the disk. Then step 4 will think the integration test is "fresh"
481    // because:
482    //
483    // - There is a valid fingerprint hash on disk (written in step 1).
484    // - The mtime of the output file (the corrupt integration executable
485    //   written in step 3) is newer than all of its dependencies.
486    // - The mtime of the integration test fingerprint dep-info file (written
487    //   in step 1) is newer than the integration test's source files, because
488    //   we haven't modified any of its source files.
489    //
490    // But the executable is corrupt and needs to be rebuilt. Clearing the
491    // fingerprint at step 3 ensures that Cargo never mistakes a partially
492    // written output as up-to-date.
493    if loc.exists() {
494        // Truncate instead of delete so that compare_old_fingerprint will
495        // still log the reason for the fingerprint failure instead of just
496        // reporting "failed to read fingerprint" during the next build if
497        // this build fails.
498        paths::write(&loc, b"")?;
499    }
500
501    let write_fingerprint = if unit.mode.is_run_custom_build() {
502        // For build scripts the `local` field of the fingerprint may change
503        // while we're executing it. For example it could be in the legacy
504        // "consider everything a dependency mode" and then we switch to "deps
505        // are explicitly specified" mode.
506        //
507        // To handle this movement we need to regenerate the `local` field of a
508        // build script's fingerprint after it's executed. We do this by
509        // using the `build_script_local_fingerprints` function which returns a
510        // thunk we can invoke on a foreign thread to calculate this.
511        let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
512        let metadata = build_runner.get_run_build_script_metadata(unit);
513        let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?;
514        let output_path = build_runner.build_explicit_deps[unit]
515            .build_script_output
516            .clone();
517        Work::new(move |_| {
518            let outputs = build_script_outputs.lock().unwrap();
519            let output = outputs
520                .get(metadata)
521                .expect("output must exist after running");
522            let deps = BuildDeps::new(&output_path, Some(output));
523
524            // FIXME: it's basically buggy that we pass `None` to `call_box`
525            // here. See documentation on `build_script_local_fingerprints`
526            // below for more information. Despite this just try to proceed and
527            // hobble along if it happens to return `Some`.
528            if let Some(new_local) = (gen_local)(&deps, None)? {
529                *fingerprint.local.lock().unwrap() = new_local;
530            }
531
532            write_fingerprint(&loc, &fingerprint)
533        })
534    } else {
535        Work::new(move |_| write_fingerprint(&loc, &fingerprint))
536    };
537
538    Ok(Job::new_dirty(write_fingerprint, dirty_reason))
539}
540
541/// Dependency edge information for fingerprints. This is generated for each
542/// dependency and is stored in a [`Fingerprint`].
543#[derive(Clone)]
544struct DepFingerprint {
545    /// The hash of the package id that this dependency points to
546    pkg_id: u64,
547    /// The crate name we're using for this dependency, which if we change we'll
548    /// need to recompile!
549    name: InternedString,
550    /// Whether or not this dependency is flagged as a public dependency or not.
551    public: bool,
552    /// Whether or not this dependency is an rmeta dependency or a "full"
553    /// dependency. In the case of an rmeta dependency our dependency edge only
554    /// actually requires the rmeta from what we depend on, so when checking
555    /// mtime information all files other than the rmeta can be ignored.
556    only_requires_rmeta: bool,
557    /// The dependency's fingerprint we recursively point to, containing all the
558    /// other hash information we'd otherwise need.
559    fingerprint: Arc<Fingerprint>,
560}
561
562/// A fingerprint can be considered to be a "short string" representing the
563/// state of a world for a package.
564///
565/// If a fingerprint ever changes, then the package itself needs to be
566/// recompiled. Inputs to the fingerprint include source code modifications,
567/// compiler flags, compiler version, etc. This structure is not simply a
568/// `String` due to the fact that some fingerprints cannot be calculated lazily.
569///
570/// Path sources, for example, use the mtime of the corresponding dep-info file
571/// as a fingerprint (all source files must be modified *before* this mtime).
572/// This dep-info file is not generated, however, until after the crate is
573/// compiled. As a result, this structure can be thought of as a fingerprint
574/// to-be. The actual value can be calculated via [`hash_u64()`], but the operation
575/// may fail as some files may not have been generated.
576///
577/// Note that dependencies are taken into account for fingerprints because rustc
578/// requires that whenever an upstream crate is recompiled that all downstream
579/// dependents are also recompiled. This is typically tracked through
580/// [`DependencyQueue`], but it also needs to be retained here because Cargo can
581/// be interrupted while executing, losing the state of the [`DependencyQueue`]
582/// graph.
583///
584/// [`hash_u64()`]: crate::core::compiler::fingerprint::Fingerprint::hash_u64
585/// [`DependencyQueue`]: crate::util::DependencyQueue
586#[derive(Serialize, Deserialize)]
587pub struct Fingerprint {
588    /// Hash of the version of `rustc` used.
589    rustc: u64,
590    /// Sorted list of cfg features enabled.
591    features: String,
592    /// Sorted list of all the declared cfg features.
593    declared_features: String,
594    /// Hash of the `Target` struct, including the target name,
595    /// package-relative source path, edition, etc.
596    target: u64,
597    /// Hash of the [`Profile`], [`CompileMode`], and any extra flags passed via
598    /// `cargo rustc` or `cargo rustdoc`.
599    ///
600    /// [`Profile`]: crate::core::profiles::Profile
601    /// [`CompileMode`]: crate::core::compiler::CompileMode
602    profile: u64,
603    /// Hash of the path to the base source file. This is relative to the
604    /// workspace root for path members, or absolute for other sources.
605    path: u64,
606    /// Fingerprints of dependencies.
607    deps: Vec<DepFingerprint>,
608    /// Information about the inputs that affect this Unit (such as source
609    /// file mtimes or build script environment variables).
610    local: Mutex<Vec<LocalFingerprint>>,
611    /// Cached hash of the [`Fingerprint`] struct. Used to improve performance
612    /// for hashing.
613    #[serde(skip)]
614    memoized_hash: Mutex<Option<u64>>,
615    /// RUSTFLAGS/RUSTDOCFLAGS environment variable value (or config value).
616    rustflags: Vec<String>,
617    /// Hash of various config settings that change how things are compiled.
618    config: u64,
619    /// The rustc target. This is only relevant for `.json` files, otherwise
620    /// the metadata hash segregates the units.
621    compile_kind: u64,
622    /// Description of whether the filesystem status for this unit is up to date
623    /// or should be considered stale.
624    #[serde(skip)]
625    fs_status: FsStatus,
626    /// Files, relative to `target_root`, that are produced by the step that
627    /// this `Fingerprint` represents. This is used to detect when the whole
628    /// fingerprint is out of date if this is missing, or if previous
629    /// fingerprints output files are regenerated and look newer than this one.
630    #[serde(skip)]
631    outputs: Vec<PathBuf>,
632}
633
634/// Indication of the status on the filesystem for a particular unit.
635#[derive(Clone, Default, Debug)]
636pub enum FsStatus {
637    /// This unit is to be considered stale, even if hash information all
638    /// matches.
639    #[default]
640    Stale,
641
642    /// File system inputs have changed (or are missing), or there were
643    /// changes to the environment variables that affect this unit. See
644    /// the variants of [`StaleItem`] for more information.
645    StaleItem(StaleItem),
646
647    /// A dependency was stale.
648    StaleDependency {
649        name: InternedString,
650        dep_mtime: FileTime,
651        max_mtime: FileTime,
652    },
653
654    /// A dependency was stale.
655    StaleDepFingerprint { name: InternedString },
656
657    /// This unit is up-to-date. All outputs and their corresponding mtime are
658    /// listed in the payload here for other dependencies to compare against.
659    UpToDate { mtimes: HashMap<PathBuf, FileTime> },
660}
661
662impl FsStatus {
663    fn up_to_date(&self) -> bool {
664        match self {
665            FsStatus::UpToDate { .. } => true,
666            FsStatus::Stale
667            | FsStatus::StaleItem(_)
668            | FsStatus::StaleDependency { .. }
669            | FsStatus::StaleDepFingerprint { .. } => false,
670        }
671    }
672}
673
674impl Serialize for DepFingerprint {
675    fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
676    where
677        S: ser::Serializer,
678    {
679        (
680            &self.pkg_id,
681            &self.name,
682            &self.public,
683            &self.fingerprint.hash_u64(),
684        )
685            .serialize(ser)
686    }
687}
688
689impl<'de> Deserialize<'de> for DepFingerprint {
690    fn deserialize<D>(d: D) -> Result<DepFingerprint, D::Error>
691    where
692        D: de::Deserializer<'de>,
693    {
694        let (pkg_id, name, public, hash) = <(u64, String, bool, u64)>::deserialize(d)?;
695        Ok(DepFingerprint {
696            pkg_id,
697            name: InternedString::new(&name),
698            public,
699            fingerprint: Arc::new(Fingerprint {
700                memoized_hash: Mutex::new(Some(hash)),
701                ..Fingerprint::new()
702            }),
703            // This field is never read since it's only used in
704            // `check_filesystem` which isn't used by fingerprints loaded from
705            // disk.
706            only_requires_rmeta: false,
707        })
708    }
709}
710
711/// A `LocalFingerprint` represents something that we use to detect direct
712/// changes to a `Fingerprint`.
713///
714/// This is where we track file information, env vars, etc. This
715/// `LocalFingerprint` struct is hashed and if the hash changes will force a
716/// recompile of any fingerprint it's included into. Note that the "local"
717/// terminology comes from the fact that it only has to do with one crate, and
718/// `Fingerprint` tracks the transitive propagation of fingerprint changes.
719///
720/// Note that because this is hashed its contents are carefully managed. Like
721/// mentioned in the above module docs, we don't want to hash absolute paths or
722/// mtime information.
723///
724/// Also note that a `LocalFingerprint` is used in `check_filesystem` to detect
725/// when the filesystem contains stale information (based on mtime currently).
726/// The paths here don't change much between compilations but they're used as
727/// inputs when we probe the filesystem looking at information.
728#[derive(Debug, Serialize, Deserialize, Hash)]
729enum LocalFingerprint {
730    /// This is a precalculated fingerprint which has an opaque string we just
731    /// hash as usual. This variant is primarily used for rustdoc where we
732    /// don't have a dep-info file to compare against.
733    ///
734    /// This is also used for build scripts with no `rerun-if-*` statements, but
735    /// that's overall a mistake and causes bugs in Cargo. We shouldn't use this
736    /// for build scripts.
737    Precalculated(String),
738
739    /// This is used for crate compilations. The `dep_info` file is a relative
740    /// path anchored at `target_root(...)` to the dep-info file that Cargo
741    /// generates (which is a custom serialization after parsing rustc's own
742    /// `dep-info` output).
743    ///
744    /// The `dep_info` file, when present, also lists a number of other files
745    /// for us to look at. If any of those files are newer than this file then
746    /// we need to recompile.
747    ///
748    /// If the `checksum` bool is true then the `dep_info` file is expected to
749    /// contain file checksums instead of file mtimes.
750    CheckDepInfo { dep_info: PathBuf, checksum: bool },
751
752    /// This represents a nonempty set of `rerun-if-changed` annotations printed
753    /// out by a build script. The `output` file is a relative file anchored at
754    /// `target_root(...)` which is the actual output of the build script. That
755    /// output has already been parsed and the paths printed out via
756    /// `rerun-if-changed` are listed in `paths`. The `paths` field is relative
757    /// to `pkg.root()`
758    ///
759    /// This is considered up-to-date if all of the `paths` are older than
760    /// `output`, otherwise we need to recompile.
761    RerunIfChanged {
762        output: PathBuf,
763        paths: Vec<PathBuf>,
764    },
765
766    /// This represents a single `rerun-if-env-changed` annotation printed by a
767    /// build script. The exact env var and value are hashed here. There's no
768    /// filesystem dependence here, and if the values are changed the hash will
769    /// change forcing a recompile.
770    RerunIfEnvChanged { var: String, val: Option<String> },
771}
772
773/// See [`FsStatus::StaleItem`].
774#[derive(Clone, Debug)]
775pub enum StaleItem {
776    MissingFile(PathBuf),
777    UnableToReadFile(PathBuf),
778    FailedToReadMetadata(PathBuf),
779    FileSizeChanged {
780        path: PathBuf,
781        old_size: u64,
782        new_size: u64,
783    },
784    ChangedFile {
785        reference: PathBuf,
786        reference_mtime: FileTime,
787        stale: PathBuf,
788        stale_mtime: FileTime,
789    },
790    ChangedChecksum {
791        source: PathBuf,
792        stored_checksum: Checksum,
793        new_checksum: Checksum,
794    },
795    MissingChecksum(PathBuf),
796    ChangedEnv {
797        var: String,
798        previous: Option<String>,
799        current: Option<String>,
800    },
801}
802
803impl LocalFingerprint {
804    /// Read the environment variable of the given env `key`, and creates a new
805    /// [`LocalFingerprint::RerunIfEnvChanged`] for it. The `env_config` is used firstly
806    /// to check if the env var is set in the config system as some envs need to be overridden.
807    /// If not, it will fallback to `std::env::var`.
808    ///
809    // TODO: `std::env::var` is allowed at this moment. Should figure out
810    // if it makes sense if permitting to read env from the env snapshot.
811    #[allow(clippy::disallowed_methods)]
812    fn from_env<K: AsRef<str>>(
813        key: K,
814        env_config: &Arc<HashMap<String, OsString>>,
815    ) -> LocalFingerprint {
816        let key = key.as_ref();
817        let var = key.to_owned();
818        let val = if let Some(val) = env_config.get(key) {
819            val.to_str().map(ToOwned::to_owned)
820        } else {
821            env::var(key).ok()
822        };
823        LocalFingerprint::RerunIfEnvChanged { var, val }
824    }
825
826    /// Checks dynamically at runtime if this `LocalFingerprint` has a stale
827    /// item inside of it.
828    ///
829    /// The main purpose of this function is to handle two different ways
830    /// fingerprints can be invalidated:
831    ///
832    /// * One is a dependency listed in rustc's dep-info files is invalid. Note
833    ///   that these could either be env vars or files. We check both here.
834    ///
835    /// * Another is the `rerun-if-changed` directive from build scripts. This
836    ///   is where we'll find whether files have actually changed
837    fn find_stale_item(
838        &self,
839        mtime_cache: &mut HashMap<PathBuf, FileTime>,
840        checksum_cache: &mut HashMap<PathBuf, Checksum>,
841        pkg: &Package,
842        target_root: &Path,
843        cargo_exe: &Path,
844        gctx: &GlobalContext,
845    ) -> CargoResult<Option<StaleItem>> {
846        let pkg_root = pkg.root();
847        match self {
848            // We need to parse `dep_info`, learn about the crate's dependencies.
849            //
850            // For each env var we see if our current process's env var still
851            // matches, and for each file we see if any of them are newer than
852            // the `dep_info` file itself whose mtime represents the start of
853            // rustc.
854            LocalFingerprint::CheckDepInfo { dep_info, checksum } => {
855                let dep_info = target_root.join(dep_info);
856                let Some(info) = parse_dep_info(pkg_root, target_root, &dep_info)? else {
857                    return Ok(Some(StaleItem::MissingFile(dep_info)));
858                };
859                for (key, previous) in info.env.iter() {
860                    if let Some(value) = pkg.manifest().metadata().env_var(key.as_str()) {
861                        if Some(value.as_ref()) == previous.as_deref() {
862                            continue;
863                        }
864                    }
865
866                    let current = if key == CARGO_ENV {
867                        Some(cargo_exe.to_str().ok_or_else(|| {
868                            format_err!(
869                                "cargo exe path {} must be valid UTF-8",
870                                cargo_exe.display()
871                            )
872                        })?)
873                    } else {
874                        if let Some(value) = gctx.env_config()?.get(key) {
875                            value.to_str()
876                        } else {
877                            gctx.get_env(key).ok()
878                        }
879                    };
880                    if current == previous.as_deref() {
881                        continue;
882                    }
883                    return Ok(Some(StaleItem::ChangedEnv {
884                        var: key.clone(),
885                        previous: previous.clone(),
886                        current: current.map(Into::into),
887                    }));
888                }
889                if *checksum {
890                    Ok(find_stale_file(
891                        mtime_cache,
892                        checksum_cache,
893                        &dep_info,
894                        info.files.iter().map(|(file, checksum)| (file, *checksum)),
895                        *checksum,
896                    ))
897                } else {
898                    Ok(find_stale_file(
899                        mtime_cache,
900                        checksum_cache,
901                        &dep_info,
902                        info.files.into_keys().map(|p| (p, None)),
903                        *checksum,
904                    ))
905                }
906            }
907
908            // We need to verify that no paths listed in `paths` are newer than
909            // the `output` path itself, or the last time the build script ran.
910            LocalFingerprint::RerunIfChanged { output, paths } => Ok(find_stale_file(
911                mtime_cache,
912                checksum_cache,
913                &target_root.join(output),
914                paths.iter().map(|p| (pkg_root.join(p), None)),
915                false,
916            )),
917
918            // These have no dependencies on the filesystem, and their values
919            // are included natively in the `Fingerprint` hash so nothing
920            // tocheck for here.
921            LocalFingerprint::RerunIfEnvChanged { .. } => Ok(None),
922            LocalFingerprint::Precalculated(..) => Ok(None),
923        }
924    }
925
926    fn kind(&self) -> &'static str {
927        match self {
928            LocalFingerprint::Precalculated(..) => "precalculated",
929            LocalFingerprint::CheckDepInfo { .. } => "dep-info",
930            LocalFingerprint::RerunIfChanged { .. } => "rerun-if-changed",
931            LocalFingerprint::RerunIfEnvChanged { .. } => "rerun-if-env-changed",
932        }
933    }
934}
935
936impl Fingerprint {
937    fn new() -> Fingerprint {
938        Fingerprint {
939            rustc: 0,
940            target: 0,
941            profile: 0,
942            path: 0,
943            features: String::new(),
944            declared_features: String::new(),
945            deps: Vec::new(),
946            local: Mutex::new(Vec::new()),
947            memoized_hash: Mutex::new(None),
948            rustflags: Vec::new(),
949            config: 0,
950            compile_kind: 0,
951            fs_status: FsStatus::Stale,
952            outputs: Vec::new(),
953        }
954    }
955
956    /// For performance reasons fingerprints will memoize their own hash, but
957    /// there's also internal mutability with its `local` field which can
958    /// change, for example with build scripts, during a build.
959    ///
960    /// This method can be used to bust all memoized hashes just before a build
961    /// to ensure that after a build completes everything is up-to-date.
962    pub fn clear_memoized(&self) {
963        *self.memoized_hash.lock().unwrap() = None;
964    }
965
966    fn hash_u64(&self) -> u64 {
967        if let Some(s) = *self.memoized_hash.lock().unwrap() {
968            return s;
969        }
970        let ret = util::hash_u64(self);
971        *self.memoized_hash.lock().unwrap() = Some(ret);
972        ret
973    }
974
975    /// Compares this fingerprint with an old version which was previously
976    /// serialized to filesystem.
977    ///
978    /// The purpose of this is exclusively to produce a diagnostic message
979    /// [`DirtyReason`], indicating why we're recompiling something.
980    fn compare(&self, old: &Fingerprint) -> DirtyReason {
981        if self.rustc != old.rustc {
982            return DirtyReason::RustcChanged;
983        }
984        if self.features != old.features {
985            return DirtyReason::FeaturesChanged {
986                old: old.features.clone(),
987                new: self.features.clone(),
988            };
989        }
990        if self.declared_features != old.declared_features {
991            return DirtyReason::DeclaredFeaturesChanged {
992                old: old.declared_features.clone(),
993                new: self.declared_features.clone(),
994            };
995        }
996        if self.target != old.target {
997            return DirtyReason::TargetConfigurationChanged;
998        }
999        if self.path != old.path {
1000            return DirtyReason::PathToSourceChanged;
1001        }
1002        if self.profile != old.profile {
1003            return DirtyReason::ProfileConfigurationChanged;
1004        }
1005        if self.rustflags != old.rustflags {
1006            return DirtyReason::RustflagsChanged {
1007                old: old.rustflags.clone(),
1008                new: self.rustflags.clone(),
1009            };
1010        }
1011        if self.config != old.config {
1012            return DirtyReason::ConfigSettingsChanged;
1013        }
1014        if self.compile_kind != old.compile_kind {
1015            return DirtyReason::CompileKindChanged;
1016        }
1017        let my_local = self.local.lock().unwrap();
1018        let old_local = old.local.lock().unwrap();
1019        if my_local.len() != old_local.len() {
1020            return DirtyReason::LocalLengthsChanged;
1021        }
1022        for (new, old) in my_local.iter().zip(old_local.iter()) {
1023            match (new, old) {
1024                (LocalFingerprint::Precalculated(a), LocalFingerprint::Precalculated(b)) => {
1025                    if a != b {
1026                        return DirtyReason::PrecalculatedComponentsChanged {
1027                            old: b.to_string(),
1028                            new: a.to_string(),
1029                        };
1030                    }
1031                }
1032                (
1033                    LocalFingerprint::CheckDepInfo {
1034                        dep_info: adep,
1035                        checksum: checksum_a,
1036                    },
1037                    LocalFingerprint::CheckDepInfo {
1038                        dep_info: bdep,
1039                        checksum: checksum_b,
1040                    },
1041                ) => {
1042                    if adep != bdep {
1043                        return DirtyReason::DepInfoOutputChanged {
1044                            old: bdep.clone(),
1045                            new: adep.clone(),
1046                        };
1047                    }
1048                    if checksum_a != checksum_b {
1049                        return DirtyReason::ChecksumUseChanged { old: *checksum_b };
1050                    }
1051                }
1052                (
1053                    LocalFingerprint::RerunIfChanged {
1054                        output: aout,
1055                        paths: apaths,
1056                    },
1057                    LocalFingerprint::RerunIfChanged {
1058                        output: bout,
1059                        paths: bpaths,
1060                    },
1061                ) => {
1062                    if aout != bout {
1063                        return DirtyReason::RerunIfChangedOutputFileChanged {
1064                            old: bout.clone(),
1065                            new: aout.clone(),
1066                        };
1067                    }
1068                    if apaths != bpaths {
1069                        return DirtyReason::RerunIfChangedOutputPathsChanged {
1070                            old: bpaths.clone(),
1071                            new: apaths.clone(),
1072                        };
1073                    }
1074                }
1075                (
1076                    LocalFingerprint::RerunIfEnvChanged {
1077                        var: akey,
1078                        val: avalue,
1079                    },
1080                    LocalFingerprint::RerunIfEnvChanged {
1081                        var: bkey,
1082                        val: bvalue,
1083                    },
1084                ) => {
1085                    if *akey != *bkey {
1086                        return DirtyReason::EnvVarsChanged {
1087                            old: bkey.clone(),
1088                            new: akey.clone(),
1089                        };
1090                    }
1091                    if *avalue != *bvalue {
1092                        return DirtyReason::EnvVarChanged {
1093                            name: akey.clone(),
1094                            old_value: bvalue.clone(),
1095                            new_value: avalue.clone(),
1096                        };
1097                    }
1098                }
1099                (a, b) => {
1100                    return DirtyReason::LocalFingerprintTypeChanged {
1101                        old: b.kind(),
1102                        new: a.kind(),
1103                    }
1104                }
1105            }
1106        }
1107
1108        if self.deps.len() != old.deps.len() {
1109            return DirtyReason::NumberOfDependenciesChanged {
1110                old: old.deps.len(),
1111                new: self.deps.len(),
1112            };
1113        }
1114        for (a, b) in self.deps.iter().zip(old.deps.iter()) {
1115            if a.name != b.name {
1116                return DirtyReason::UnitDependencyNameChanged {
1117                    old: b.name,
1118                    new: a.name,
1119                };
1120            }
1121
1122            if a.fingerprint.hash_u64() != b.fingerprint.hash_u64() {
1123                return DirtyReason::UnitDependencyInfoChanged {
1124                    new_name: a.name,
1125                    new_fingerprint: a.fingerprint.hash_u64(),
1126                    old_name: b.name,
1127                    old_fingerprint: b.fingerprint.hash_u64(),
1128                };
1129            }
1130        }
1131
1132        if !self.fs_status.up_to_date() {
1133            return DirtyReason::FsStatusOutdated(self.fs_status.clone());
1134        }
1135
1136        // This typically means some filesystem modifications happened or
1137        // something transitive was odd. In general we should strive to provide
1138        // a better error message than this, so if you see this message a lot it
1139        // likely means this method needs to be updated!
1140        DirtyReason::NothingObvious
1141    }
1142
1143    /// Dynamically inspect the local filesystem to update the `fs_status` field
1144    /// of this `Fingerprint`.
1145    ///
1146    /// This function is used just after a `Fingerprint` is constructed to check
1147    /// the local state of the filesystem and propagate any dirtiness from
1148    /// dependencies up to this unit as well. This function assumes that the
1149    /// unit starts out as [`FsStatus::Stale`] and then it will optionally switch
1150    /// it to `UpToDate` if it can.
1151    fn check_filesystem(
1152        &mut self,
1153        mtime_cache: &mut HashMap<PathBuf, FileTime>,
1154        checksum_cache: &mut HashMap<PathBuf, Checksum>,
1155        pkg: &Package,
1156        target_root: &Path,
1157        cargo_exe: &Path,
1158        gctx: &GlobalContext,
1159    ) -> CargoResult<()> {
1160        assert!(!self.fs_status.up_to_date());
1161
1162        let pkg_root = pkg.root();
1163        let mut mtimes = HashMap::new();
1164
1165        // Get the `mtime` of all outputs. Optionally update their mtime
1166        // afterwards based on the `mtime_on_use` flag. Afterwards we want the
1167        // minimum mtime as it's the one we'll be comparing to inputs and
1168        // dependencies.
1169        for output in self.outputs.iter() {
1170            let mtime = match paths::mtime(output) {
1171                Ok(mtime) => mtime,
1172
1173                // This path failed to report its `mtime`. It probably doesn't
1174                // exists, so leave ourselves as stale and bail out.
1175                Err(e) => {
1176                    debug!("failed to get mtime of {:?}: {}", output, e);
1177                    return Ok(());
1178                }
1179            };
1180            assert!(mtimes.insert(output.clone(), mtime).is_none());
1181        }
1182
1183        let opt_max = mtimes.iter().max_by_key(|kv| kv.1);
1184        let Some((max_path, max_mtime)) = opt_max else {
1185            // We had no output files. This means we're an overridden build
1186            // script and we're just always up to date because we aren't
1187            // watching the filesystem.
1188            self.fs_status = FsStatus::UpToDate { mtimes };
1189            return Ok(());
1190        };
1191        debug!(
1192            "max output mtime for {:?} is {:?} {}",
1193            pkg_root, max_path, max_mtime
1194        );
1195
1196        for dep in self.deps.iter() {
1197            let dep_mtimes = match &dep.fingerprint.fs_status {
1198                FsStatus::UpToDate { mtimes } => mtimes,
1199                // If our dependency is stale, so are we, so bail out.
1200                FsStatus::Stale
1201                | FsStatus::StaleItem(_)
1202                | FsStatus::StaleDependency { .. }
1203                | FsStatus::StaleDepFingerprint { .. } => {
1204                    self.fs_status = FsStatus::StaleDepFingerprint { name: dep.name };
1205                    return Ok(());
1206                }
1207            };
1208
1209            // If our dependency edge only requires the rmeta file to be present
1210            // then we only need to look at that one output file, otherwise we
1211            // need to consider all output files to see if we're out of date.
1212            let (dep_path, dep_mtime) = if dep.only_requires_rmeta {
1213                dep_mtimes
1214                    .iter()
1215                    .find(|(path, _mtime)| {
1216                        path.extension().and_then(|s| s.to_str()) == Some("rmeta")
1217                    })
1218                    .expect("failed to find rmeta")
1219            } else {
1220                match dep_mtimes.iter().max_by_key(|kv| kv.1) {
1221                    Some(dep_mtime) => dep_mtime,
1222                    // If our dependencies is up to date and has no filesystem
1223                    // interactions, then we can move on to the next dependency.
1224                    None => continue,
1225                }
1226            };
1227            debug!(
1228                "max dep mtime for {:?} is {:?} {}",
1229                pkg_root, dep_path, dep_mtime
1230            );
1231
1232            // If the dependency is newer than our own output then it was
1233            // recompiled previously. We transitively become stale ourselves in
1234            // that case, so bail out.
1235            //
1236            // Note that this comparison should probably be `>=`, not `>`, but
1237            // for a discussion of why it's `>` see the discussion about #5918
1238            // below in `find_stale`.
1239            if dep_mtime > max_mtime {
1240                info!(
1241                    "dependency on `{}` is newer than we are {} > {} {:?}",
1242                    dep.name, dep_mtime, max_mtime, pkg_root
1243                );
1244
1245                self.fs_status = FsStatus::StaleDependency {
1246                    name: dep.name,
1247                    dep_mtime: *dep_mtime,
1248                    max_mtime: *max_mtime,
1249                };
1250
1251                return Ok(());
1252            }
1253        }
1254
1255        // If we reached this far then all dependencies are up to date. Check
1256        // all our `LocalFingerprint` information to see if we have any stale
1257        // files for this package itself. If we do find something log a helpful
1258        // message and bail out so we stay stale.
1259        for local in self.local.get_mut().unwrap().iter() {
1260            if let Some(item) = local.find_stale_item(
1261                mtime_cache,
1262                checksum_cache,
1263                pkg,
1264                target_root,
1265                cargo_exe,
1266                gctx,
1267            )? {
1268                item.log();
1269                self.fs_status = FsStatus::StaleItem(item);
1270                return Ok(());
1271            }
1272        }
1273
1274        // Everything was up to date! Record such.
1275        self.fs_status = FsStatus::UpToDate { mtimes };
1276        debug!("filesystem up-to-date {:?}", pkg_root);
1277
1278        Ok(())
1279    }
1280}
1281
1282impl hash::Hash for Fingerprint {
1283    fn hash<H: Hasher>(&self, h: &mut H) {
1284        let Fingerprint {
1285            rustc,
1286            ref features,
1287            ref declared_features,
1288            target,
1289            path,
1290            profile,
1291            ref deps,
1292            ref local,
1293            config,
1294            compile_kind,
1295            ref rustflags,
1296            ..
1297        } = *self;
1298        let local = local.lock().unwrap();
1299        (
1300            rustc,
1301            features,
1302            declared_features,
1303            target,
1304            path,
1305            profile,
1306            &*local,
1307            config,
1308            compile_kind,
1309            rustflags,
1310        )
1311            .hash(h);
1312
1313        h.write_usize(deps.len());
1314        for DepFingerprint {
1315            pkg_id,
1316            name,
1317            public,
1318            fingerprint,
1319            only_requires_rmeta: _, // static property, no need to hash
1320        } in deps
1321        {
1322            pkg_id.hash(h);
1323            name.hash(h);
1324            public.hash(h);
1325            // use memoized dep hashes to avoid exponential blowup
1326            h.write_u64(fingerprint.hash_u64());
1327        }
1328    }
1329}
1330
1331impl DepFingerprint {
1332    fn new(
1333        build_runner: &mut BuildRunner<'_, '_>,
1334        parent: &Unit,
1335        dep: &UnitDep,
1336    ) -> CargoResult<DepFingerprint> {
1337        let fingerprint = calculate(build_runner, &dep.unit)?;
1338        // We need to be careful about what we hash here. We have a goal of
1339        // supporting renaming a project directory and not rebuilding
1340        // everything. To do that, however, we need to make sure that the cwd
1341        // doesn't make its way into any hashes, and one source of that is the
1342        // `SourceId` for `path` packages.
1343        //
1344        // We already have a requirement that `path` packages all have unique
1345        // names (sort of for this same reason), so if the package source is a
1346        // `path` then we just hash the name, but otherwise we hash the full
1347        // id as it won't change when the directory is renamed.
1348        let pkg_id = if dep.unit.pkg.package_id().source_id().is_path() {
1349            util::hash_u64(dep.unit.pkg.package_id().name())
1350        } else {
1351            util::hash_u64(dep.unit.pkg.package_id())
1352        };
1353
1354        Ok(DepFingerprint {
1355            pkg_id,
1356            name: dep.extern_crate_name,
1357            public: dep.public,
1358            fingerprint,
1359            only_requires_rmeta: build_runner.only_requires_rmeta(parent, &dep.unit),
1360        })
1361    }
1362}
1363
1364impl StaleItem {
1365    /// Use the `log` crate to log a hopefully helpful message in diagnosing
1366    /// what file is considered stale and why. This is intended to be used in
1367    /// conjunction with `CARGO_LOG` to determine why Cargo is recompiling
1368    /// something. Currently there's no user-facing usage of this other than
1369    /// that.
1370    fn log(&self) {
1371        match self {
1372            StaleItem::MissingFile(path) => {
1373                info!("stale: missing {:?}", path);
1374            }
1375            StaleItem::UnableToReadFile(path) => {
1376                info!("stale: unable to read {:?}", path);
1377            }
1378            StaleItem::FailedToReadMetadata(path) => {
1379                info!("stale: couldn't read metadata {:?}", path);
1380            }
1381            StaleItem::ChangedFile {
1382                reference,
1383                reference_mtime,
1384                stale,
1385                stale_mtime,
1386            } => {
1387                info!("stale: changed {:?}", stale);
1388                info!("          (vs) {:?}", reference);
1389                info!("               {:?} < {:?}", reference_mtime, stale_mtime);
1390            }
1391            StaleItem::FileSizeChanged {
1392                path,
1393                new_size,
1394                old_size,
1395            } => {
1396                info!("stale: changed {:?}", path);
1397                info!("prior file size {old_size}");
1398                info!("  new file size {new_size}");
1399            }
1400            StaleItem::ChangedChecksum {
1401                source,
1402                stored_checksum,
1403                new_checksum,
1404            } => {
1405                info!("stale: changed {:?}", source);
1406                info!("prior checksum {stored_checksum}");
1407                info!("  new checksum {new_checksum}");
1408            }
1409            StaleItem::MissingChecksum(path) => {
1410                info!("stale: no prior checksum {:?}", path);
1411            }
1412            StaleItem::ChangedEnv {
1413                var,
1414                previous,
1415                current,
1416            } => {
1417                info!("stale: changed env {:?}", var);
1418                info!("       {:?} != {:?}", previous, current);
1419            }
1420        }
1421    }
1422}
1423
1424/// Calculates the fingerprint for a [`Unit`].
1425///
1426/// This fingerprint is used by Cargo to learn about when information such as:
1427///
1428/// * A non-path package changes (changes version, changes revision, etc).
1429/// * Any dependency changes
1430/// * The compiler changes
1431/// * The set of features a package is built with changes
1432/// * The profile a target is compiled with changes (e.g., opt-level changes)
1433/// * Any other compiler flags change that will affect the result
1434///
1435/// Information like file modification time is only calculated for path
1436/// dependencies.
1437fn calculate(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Arc<Fingerprint>> {
1438    // This function is slammed quite a lot, so the result is memoized.
1439    if let Some(s) = build_runner.fingerprints.get(unit) {
1440        return Ok(Arc::clone(s));
1441    }
1442    let mut fingerprint = if unit.mode.is_run_custom_build() {
1443        calculate_run_custom_build(build_runner, unit)?
1444    } else if unit.mode.is_doc_test() {
1445        panic!("doc tests do not fingerprint");
1446    } else {
1447        calculate_normal(build_runner, unit)?
1448    };
1449
1450    // After we built the initial `Fingerprint` be sure to update the
1451    // `fs_status` field of it.
1452    let target_root = target_root(build_runner);
1453    let cargo_exe = build_runner.bcx.gctx.cargo_exe()?;
1454    fingerprint.check_filesystem(
1455        &mut build_runner.mtime_cache,
1456        &mut build_runner.checksum_cache,
1457        &unit.pkg,
1458        &target_root,
1459        cargo_exe,
1460        build_runner.bcx.gctx,
1461    )?;
1462
1463    let fingerprint = Arc::new(fingerprint);
1464    build_runner
1465        .fingerprints
1466        .insert(unit.clone(), Arc::clone(&fingerprint));
1467    Ok(fingerprint)
1468}
1469
1470/// Calculate a fingerprint for a "normal" unit, or anything that's not a build
1471/// script. This is an internal helper of [`calculate`], don't call directly.
1472fn calculate_normal(
1473    build_runner: &mut BuildRunner<'_, '_>,
1474    unit: &Unit,
1475) -> CargoResult<Fingerprint> {
1476    let deps = {
1477        // Recursively calculate the fingerprint for all of our dependencies.
1478        //
1479        // Skip fingerprints of binaries because they don't actually induce a
1480        // recompile, they're just dependencies in the sense that they need to be
1481        // built. The only exception here are artifact dependencies,
1482        // which is an actual dependency that needs a recompile.
1483        //
1484        // Create Vec since mutable build_runner is needed in closure.
1485        let deps = Vec::from(build_runner.unit_deps(unit));
1486        let mut deps = deps
1487            .into_iter()
1488            .filter(|dep| !dep.unit.target.is_bin() || dep.unit.artifact.is_true())
1489            .map(|dep| DepFingerprint::new(build_runner, unit, &dep))
1490            .collect::<CargoResult<Vec<_>>>()?;
1491        deps.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id));
1492        deps
1493    };
1494
1495    // Afterwards calculate our own fingerprint information.
1496    let target_root = target_root(build_runner);
1497    let local = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
1498        // rustdoc does not have dep-info files.
1499        let fingerprint = pkg_fingerprint(build_runner.bcx, &unit.pkg).with_context(|| {
1500            format!(
1501                "failed to determine package fingerprint for documenting {}",
1502                unit.pkg
1503            )
1504        })?;
1505        vec![LocalFingerprint::Precalculated(fingerprint)]
1506    } else {
1507        let dep_info = dep_info_loc(build_runner, unit);
1508        let dep_info = dep_info.strip_prefix(&target_root).unwrap().to_path_buf();
1509        vec![LocalFingerprint::CheckDepInfo {
1510            dep_info,
1511            checksum: build_runner.bcx.gctx.cli_unstable().checksum_freshness,
1512        }]
1513    };
1514
1515    // Figure out what the outputs of our unit is, and we'll be storing them
1516    // into the fingerprint as well.
1517    let outputs = build_runner
1518        .outputs(unit)?
1519        .iter()
1520        .filter(|output| !matches!(output.flavor, FileFlavor::DebugInfo | FileFlavor::Auxiliary))
1521        .map(|output| output.path.clone())
1522        .collect();
1523
1524    // Fill out a bunch more information that we'll be tracking typically
1525    // hashed to take up less space on disk as we just need to know when things
1526    // change.
1527    let extra_flags = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
1528        &unit.rustdocflags
1529    } else {
1530        &unit.rustflags
1531    }
1532    .to_vec();
1533
1534    let profile_hash = util::hash_u64((
1535        &unit.profile,
1536        unit.mode,
1537        build_runner.bcx.extra_args_for(unit),
1538        build_runner.lto[unit],
1539        unit.pkg.manifest().lint_rustflags(),
1540    ));
1541    let mut config = StableHasher::new();
1542    if let Some(linker) = build_runner.compilation.target_linker(unit.kind) {
1543        linker.hash(&mut config);
1544    }
1545    if unit.mode.is_doc() && build_runner.bcx.gctx.cli_unstable().rustdoc_map {
1546        if let Ok(map) = build_runner.bcx.gctx.doc_extern_map() {
1547            map.hash(&mut config);
1548        }
1549    }
1550    if let Some(allow_features) = &build_runner.bcx.gctx.cli_unstable().allow_features {
1551        allow_features.hash(&mut config);
1552    }
1553    let compile_kind = unit.kind.fingerprint_hash();
1554    let mut declared_features = unit.pkg.summary().features().keys().collect::<Vec<_>>();
1555    declared_features.sort(); // to avoid useless rebuild if the user orders it's features
1556                              // differently
1557    Ok(Fingerprint {
1558        rustc: util::hash_u64(&build_runner.bcx.rustc().verbose_version),
1559        target: util::hash_u64(&unit.target),
1560        profile: profile_hash,
1561        // Note that .0 is hashed here, not .1 which is the cwd. That doesn't
1562        // actually affect the output artifact so there's no need to hash it.
1563        path: util::hash_u64(path_args(build_runner.bcx.ws, unit).0),
1564        features: format!("{:?}", unit.features),
1565        declared_features: format!("{declared_features:?}"),
1566        deps,
1567        local: Mutex::new(local),
1568        memoized_hash: Mutex::new(None),
1569        config: Hasher::finish(&config),
1570        compile_kind,
1571        rustflags: extra_flags,
1572        fs_status: FsStatus::Stale,
1573        outputs,
1574    })
1575}
1576
1577/// Calculate a fingerprint for an "execute a build script" unit.  This is an
1578/// internal helper of [`calculate`], don't call directly.
1579fn calculate_run_custom_build(
1580    build_runner: &mut BuildRunner<'_, '_>,
1581    unit: &Unit,
1582) -> CargoResult<Fingerprint> {
1583    assert!(unit.mode.is_run_custom_build());
1584    // Using the `BuildDeps` information we'll have previously parsed and
1585    // inserted into `build_explicit_deps` built an initial snapshot of the
1586    // `LocalFingerprint` list for this build script. If we previously executed
1587    // the build script this means we'll be watching files and env vars.
1588    // Otherwise if we haven't previously executed it we'll just start watching
1589    // the whole crate.
1590    let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?;
1591    let deps = &build_runner.build_explicit_deps[unit];
1592    let local = (gen_local)(
1593        deps,
1594        Some(&|| {
1595            const IO_ERR_MESSAGE: &str = "\
1596An I/O error happened. Please make sure you can access the file.
1597
1598By default, if your project contains a build script, cargo scans all files in
1599it to determine whether a rebuild is needed. If you don't expect to access the
1600file, specify `rerun-if-changed` in your build script.
1601See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed for more information.";
1602            pkg_fingerprint(build_runner.bcx, &unit.pkg).map_err(|err| {
1603                let mut message = format!("failed to determine package fingerprint for build script for {}", unit.pkg);
1604                if err.root_cause().is::<io::Error>() {
1605                    message = format!("{}\n{}", message, IO_ERR_MESSAGE)
1606                }
1607                err.context(message)
1608            })
1609        }),
1610    )?
1611    .unwrap();
1612    let output = deps.build_script_output.clone();
1613
1614    // Include any dependencies of our execution, which is typically just the
1615    // compilation of the build script itself. (if the build script changes we
1616    // should be rerun!). Note though that if we're an overridden build script
1617    // we have no dependencies so no need to recurse in that case.
1618    let deps = if overridden {
1619        // Overridden build scripts don't need to track deps.
1620        vec![]
1621    } else {
1622        // Create Vec since mutable build_runner is needed in closure.
1623        let deps = Vec::from(build_runner.unit_deps(unit));
1624        deps.into_iter()
1625            .map(|dep| DepFingerprint::new(build_runner, unit, &dep))
1626            .collect::<CargoResult<Vec<_>>>()?
1627    };
1628
1629    let rustflags = unit.rustflags.to_vec();
1630
1631    Ok(Fingerprint {
1632        local: Mutex::new(local),
1633        rustc: util::hash_u64(&build_runner.bcx.rustc().verbose_version),
1634        deps,
1635        outputs: if overridden { Vec::new() } else { vec![output] },
1636        rustflags,
1637
1638        // Most of the other info is blank here as we don't really include it
1639        // in the execution of the build script, but... this may be a latent
1640        // bug in Cargo.
1641        ..Fingerprint::new()
1642    })
1643}
1644
1645/// Get ready to compute the [`LocalFingerprint`] values
1646/// for a [`RunCustomBuild`] unit.
1647///
1648/// This function has, what's on the surface, a seriously wonky interface.
1649/// You'll call this function and it'll return a closure and a boolean. The
1650/// boolean is pretty simple in that it indicates whether the `unit` has been
1651/// overridden via `.cargo/config.toml`. The closure is much more complicated.
1652///
1653/// This closure is intended to capture any local state necessary to compute
1654/// the `LocalFingerprint` values for this unit. It is `Send` and `'static` to
1655/// be sent to other threads as well (such as when we're executing build
1656/// scripts). That deduplication is the rationale for the closure at least.
1657///
1658/// The arguments to the closure are a bit weirder, though, and I'll apologize
1659/// in advance for the weirdness too. The first argument to the closure is a
1660/// `&BuildDeps`. This is the parsed version of a build script, and when Cargo
1661/// starts up this is cached from previous runs of a build script.  After a
1662/// build script executes the output file is reparsed and passed in here.
1663///
1664/// The second argument is the weirdest, it's *optionally* a closure to
1665/// call [`pkg_fingerprint`]. The `pkg_fingerprint` requires access to
1666/// "source map" located in `Context`. That's very non-`'static` and
1667/// non-`Send`, so it can't be used on other threads, such as when we invoke
1668/// this after a build script has finished. The `Option` allows us to for sure
1669/// calculate it on the main thread at the beginning, and then swallow the bug
1670/// for now where a worker thread after a build script has finished doesn't
1671/// have access. Ideally there would be no second argument or it would be more
1672/// "first class" and not an `Option` but something that can be sent between
1673/// threads. In any case, it's a bug for now.
1674///
1675/// This isn't the greatest of interfaces, and if there's suggestions to
1676/// improve please do so!
1677///
1678/// FIXME(#6779) - see all the words above
1679///
1680/// [`RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild
1681fn build_script_local_fingerprints(
1682    build_runner: &mut BuildRunner<'_, '_>,
1683    unit: &Unit,
1684) -> CargoResult<(
1685    Box<
1686        dyn FnOnce(
1687                &BuildDeps,
1688                Option<&dyn Fn() -> CargoResult<String>>,
1689            ) -> CargoResult<Option<Vec<LocalFingerprint>>>
1690            + Send,
1691    >,
1692    bool,
1693)> {
1694    assert!(unit.mode.is_run_custom_build());
1695    // First up, if this build script is entirely overridden, then we just
1696    // return the hash of what we overrode it with. This is the easy case!
1697    if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) {
1698        debug!("override local fingerprints deps {}", unit.pkg);
1699        return Ok((
1700            Box::new(
1701                move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult<String>>| {
1702                    Ok(Some(vec![fingerprint]))
1703                },
1704            ),
1705            true, // this is an overridden build script
1706        ));
1707    }
1708
1709    // ... Otherwise this is a "real" build script and we need to return a real
1710    // closure. Our returned closure classifies the build script based on
1711    // whether it prints `rerun-if-*`. If it *doesn't* print this it's where the
1712    // magical second argument comes into play, which fingerprints a whole
1713    // package. Remember that the fact that this is an `Option` is a bug, but a
1714    // longstanding bug, in Cargo. Recent refactorings just made it painfully
1715    // obvious.
1716    let pkg_root = unit.pkg.root().to_path_buf();
1717    let target_dir = target_root(build_runner);
1718    let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
1719    let calculate =
1720        move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult<String>>| {
1721            if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() {
1722                match pkg_fingerprint {
1723                    // FIXME: this is somewhat buggy with respect to docker and
1724                    // weird filesystems. The `Precalculated` variant
1725                    // constructed below will, for `path` dependencies, contain
1726                    // a stringified version of the mtime for the local crate.
1727                    // This violates one of the things we describe in this
1728                    // module's doc comment, never hashing mtimes. We should
1729                    // figure out a better scheme where a package fingerprint
1730                    // may be a string (like for a registry) or a list of files
1731                    // (like for a path dependency). Those list of files would
1732                    // be stored here rather than the mtime of them.
1733                    Some(f) => {
1734                        let s = f()?;
1735                        debug!(
1736                            "old local fingerprints deps {:?} precalculated={:?}",
1737                            pkg_root, s
1738                        );
1739                        return Ok(Some(vec![LocalFingerprint::Precalculated(s)]));
1740                    }
1741                    None => return Ok(None),
1742                }
1743            }
1744
1745            // Ok so now we're in "new mode" where we can have files listed as
1746            // dependencies as well as env vars listed as dependencies. Process
1747            // them all here.
1748            Ok(Some(local_fingerprints_deps(
1749                deps,
1750                &target_dir,
1751                &pkg_root,
1752                &env_config,
1753            )))
1754        };
1755
1756    // Note that `false` == "not overridden"
1757    Ok((Box::new(calculate), false))
1758}
1759
1760/// Create a [`LocalFingerprint`] for an overridden build script.
1761/// Returns None if it is not overridden.
1762fn build_script_override_fingerprint(
1763    build_runner: &mut BuildRunner<'_, '_>,
1764    unit: &Unit,
1765) -> Option<LocalFingerprint> {
1766    // Build script output is only populated at this stage when it is
1767    // overridden.
1768    let build_script_outputs = build_runner.build_script_outputs.lock().unwrap();
1769    let metadata = build_runner.get_run_build_script_metadata(unit);
1770    // Returns None if it is not overridden.
1771    let output = build_script_outputs.get(metadata)?;
1772    let s = format!(
1773        "overridden build state with hash: {}",
1774        util::hash_u64(output)
1775    );
1776    Some(LocalFingerprint::Precalculated(s))
1777}
1778
1779/// Compute the [`LocalFingerprint`] values for a [`RunCustomBuild`] unit for
1780/// non-overridden new-style build scripts only. This is only used when `deps`
1781/// is already known to have a nonempty `rerun-if-*` somewhere.
1782///
1783/// [`RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild
1784fn local_fingerprints_deps(
1785    deps: &BuildDeps,
1786    target_root: &Path,
1787    pkg_root: &Path,
1788    env_config: &Arc<HashMap<String, OsString>>,
1789) -> Vec<LocalFingerprint> {
1790    debug!("new local fingerprints deps {:?}", pkg_root);
1791    let mut local = Vec::new();
1792
1793    if !deps.rerun_if_changed.is_empty() {
1794        // Note that like the module comment above says we are careful to never
1795        // store an absolute path in `LocalFingerprint`, so ensure that we strip
1796        // absolute prefixes from them.
1797        let output = deps
1798            .build_script_output
1799            .strip_prefix(target_root)
1800            .unwrap()
1801            .to_path_buf();
1802        let paths = deps
1803            .rerun_if_changed
1804            .iter()
1805            .map(|p| p.strip_prefix(pkg_root).unwrap_or(p).to_path_buf())
1806            .collect();
1807        local.push(LocalFingerprint::RerunIfChanged { output, paths });
1808    }
1809
1810    local.extend(
1811        deps.rerun_if_env_changed
1812            .iter()
1813            .map(|s| LocalFingerprint::from_env(s, env_config)),
1814    );
1815
1816    local
1817}
1818
1819/// Writes the short fingerprint hash value to `<loc>`
1820/// and logs detailed JSON information to `<loc>.json`.
1821fn write_fingerprint(loc: &Path, fingerprint: &Fingerprint) -> CargoResult<()> {
1822    debug_assert_ne!(fingerprint.rustc, 0);
1823    // fingerprint::new().rustc == 0, make sure it doesn't make it to the file system.
1824    // This is mostly so outside tools can reliably find out what rust version this file is for,
1825    // as we can use the full hash.
1826    let hash = fingerprint.hash_u64();
1827    debug!("write fingerprint ({:x}) : {}", hash, loc.display());
1828    paths::write(loc, util::to_hex(hash).as_bytes())?;
1829
1830    let json = serde_json::to_string(fingerprint).unwrap();
1831    if cfg!(debug_assertions) {
1832        let f: Fingerprint = serde_json::from_str(&json).unwrap();
1833        assert_eq!(f.hash_u64(), hash);
1834    }
1835    paths::write(&loc.with_extension("json"), json.as_bytes())?;
1836    Ok(())
1837}
1838
1839/// Prepare for work when a package starts to build
1840pub fn prepare_init(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
1841    let new1 = build_runner.files().fingerprint_dir(unit);
1842
1843    // Doc tests have no output, thus no fingerprint.
1844    if !new1.exists() && !unit.mode.is_doc_test() {
1845        paths::create_dir_all(&new1)?;
1846    }
1847
1848    Ok(())
1849}
1850
1851/// Returns the location that the dep-info file will show up at
1852/// for the [`Unit`] specified.
1853pub fn dep_info_loc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
1854    build_runner.files().fingerprint_file_path(unit, "dep-")
1855}
1856
1857/// Returns an absolute path that target directory.
1858/// All paths are rewritten to be relative to this.
1859fn target_root(build_runner: &BuildRunner<'_, '_>) -> PathBuf {
1860    build_runner.bcx.ws.target_dir().into_path_unlocked()
1861}
1862
1863/// Reads the value from the old fingerprint hash file and compare.
1864///
1865/// If dirty, it then restores the detailed information
1866/// from the fingerprint JSON file, and provides an rich dirty reason.
1867fn compare_old_fingerprint(
1868    unit: &Unit,
1869    old_hash_path: &Path,
1870    new_fingerprint: &Fingerprint,
1871    mtime_on_use: bool,
1872    forced: bool,
1873) -> Option<DirtyReason> {
1874    if mtime_on_use {
1875        // update the mtime so other cleaners know we used it
1876        let t = FileTime::from_system_time(SystemTime::now());
1877        debug!("mtime-on-use forcing {:?} to {}", old_hash_path, t);
1878        paths::set_file_time_no_err(old_hash_path, t);
1879    }
1880
1881    let compare = _compare_old_fingerprint(old_hash_path, new_fingerprint);
1882
1883    match compare.as_ref() {
1884        Ok(None) => {}
1885        Ok(Some(reason)) => {
1886            info!(
1887                "fingerprint dirty for {}/{:?}/{:?}",
1888                unit.pkg, unit.mode, unit.target,
1889            );
1890            info!("    dirty: {reason:?}");
1891        }
1892        Err(e) => {
1893            info!(
1894                "fingerprint error for {}/{:?}/{:?}",
1895                unit.pkg, unit.mode, unit.target,
1896            );
1897            info!("    err: {e:?}");
1898        }
1899    }
1900
1901    match compare {
1902        Ok(None) if forced => Some(DirtyReason::Forced),
1903        Ok(reason) => reason,
1904        Err(_) => Some(DirtyReason::FreshBuild),
1905    }
1906}
1907
1908fn _compare_old_fingerprint(
1909    old_hash_path: &Path,
1910    new_fingerprint: &Fingerprint,
1911) -> CargoResult<Option<DirtyReason>> {
1912    let old_fingerprint_short = paths::read(old_hash_path)?;
1913
1914    let new_hash = new_fingerprint.hash_u64();
1915
1916    if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() {
1917        return Ok(None);
1918    }
1919
1920    let old_fingerprint_json = paths::read(&old_hash_path.with_extension("json"))?;
1921    let old_fingerprint: Fingerprint = serde_json::from_str(&old_fingerprint_json)
1922        .with_context(|| internal("failed to deserialize json"))?;
1923    // Fingerprint can be empty after a failed rebuild (see comment in prepare_target).
1924    if !old_fingerprint_short.is_empty() {
1925        debug_assert_eq!(
1926            util::to_hex(old_fingerprint.hash_u64()),
1927            old_fingerprint_short
1928        );
1929    }
1930
1931    Ok(Some(new_fingerprint.compare(&old_fingerprint)))
1932}
1933
1934/// Calculates the fingerprint of a unit thats contains no dep-info files.
1935fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult<String> {
1936    let source_id = pkg.package_id().source_id();
1937    let sources = bcx.packages.sources();
1938
1939    let source = sources
1940        .get(source_id)
1941        .ok_or_else(|| internal("missing package source"))?;
1942    source.fingerprint(pkg)
1943}
1944
1945/// The `reference` file is considered as "stale" if any file from `paths` has a newer mtime.
1946fn find_stale_file<I, P>(
1947    mtime_cache: &mut HashMap<PathBuf, FileTime>,
1948    checksum_cache: &mut HashMap<PathBuf, Checksum>,
1949    reference: &Path,
1950    paths: I,
1951    use_checksums: bool,
1952) -> Option<StaleItem>
1953where
1954    I: IntoIterator<Item = (P, Option<(u64, Checksum)>)>,
1955    P: AsRef<Path>,
1956{
1957    let Ok(reference_mtime) = paths::mtime(reference) else {
1958        return Some(StaleItem::MissingFile(reference.to_path_buf()));
1959    };
1960
1961    let skipable_dirs = if let Ok(cargo_home) = home::cargo_home() {
1962        let skipable_dirs: Vec<_> = ["git", "registry"]
1963            .into_iter()
1964            .map(|subfolder| cargo_home.join(subfolder))
1965            .collect();
1966        Some(skipable_dirs)
1967    } else {
1968        None
1969    };
1970    for (path, prior_checksum) in paths {
1971        let path = path.as_ref();
1972
1973        // Assuming anything in cargo_home/{git, registry} is immutable
1974        // (see also #9455 about marking the src directory readonly) which avoids rebuilds when CI
1975        // caches $CARGO_HOME/registry/{index, cache} and $CARGO_HOME/git/db across runs, keeping
1976        // the content the same but changing the mtime.
1977        if let Some(ref skipable_dirs) = skipable_dirs {
1978            if skipable_dirs.iter().any(|dir| path.starts_with(dir)) {
1979                continue;
1980            }
1981        }
1982        if use_checksums {
1983            let Some((file_len, prior_checksum)) = prior_checksum else {
1984                return Some(StaleItem::MissingChecksum(path.to_path_buf()));
1985            };
1986            let path_buf = path.to_path_buf();
1987
1988            let path_checksum = match checksum_cache.entry(path_buf) {
1989                Entry::Occupied(o) => *o.get(),
1990                Entry::Vacant(v) => {
1991                    let Ok(current_file_len) = fs::metadata(&path).map(|m| m.len()) else {
1992                        return Some(StaleItem::FailedToReadMetadata(path.to_path_buf()));
1993                    };
1994                    if current_file_len != file_len {
1995                        return Some(StaleItem::FileSizeChanged {
1996                            path: path.to_path_buf(),
1997                            new_size: current_file_len,
1998                            old_size: file_len,
1999                        });
2000                    }
2001                    let Ok(file) = File::open(path) else {
2002                        return Some(StaleItem::MissingFile(path.to_path_buf()));
2003                    };
2004                    let Ok(checksum) = Checksum::compute(prior_checksum.algo(), file) else {
2005                        return Some(StaleItem::UnableToReadFile(path.to_path_buf()));
2006                    };
2007                    *v.insert(checksum)
2008                }
2009            };
2010            if path_checksum == prior_checksum {
2011                continue;
2012            }
2013            return Some(StaleItem::ChangedChecksum {
2014                source: path.to_path_buf(),
2015                stored_checksum: prior_checksum,
2016                new_checksum: path_checksum,
2017            });
2018        } else {
2019            let path_mtime = match mtime_cache.entry(path.to_path_buf()) {
2020                Entry::Occupied(o) => *o.get(),
2021                Entry::Vacant(v) => {
2022                    let Ok(mtime) = paths::mtime_recursive(path) else {
2023                        return Some(StaleItem::MissingFile(path.to_path_buf()));
2024                    };
2025                    *v.insert(mtime)
2026                }
2027            };
2028
2029            // TODO: fix #5918.
2030            // Note that equal mtimes should be considered "stale". For filesystems with
2031            // not much timestamp precision like 1s this is would be a conservative approximation
2032            // to handle the case where a file is modified within the same second after
2033            // a build starts. We want to make sure that incremental rebuilds pick that up!
2034            //
2035            // For filesystems with nanosecond precision it's been seen in the wild that
2036            // its "nanosecond precision" isn't really nanosecond-accurate. It turns out that
2037            // kernels may cache the current time so files created at different times actually
2038            // list the same nanosecond precision. Some digging on #5919 picked up that the
2039            // kernel caches the current time between timer ticks, which could mean that if
2040            // a file is updated at most 10ms after a build starts then Cargo may not
2041            // pick up the build changes.
2042            //
2043            // All in all, an equality check here would be a conservative assumption that,
2044            // if equal, files were changed just after a previous build finished.
2045            // Unfortunately this became problematic when (in #6484) cargo switch to more accurately
2046            // measuring the start time of builds.
2047            if path_mtime <= reference_mtime {
2048                continue;
2049            }
2050
2051            return Some(StaleItem::ChangedFile {
2052                reference: reference.to_path_buf(),
2053                reference_mtime,
2054                stale: path.to_path_buf(),
2055                stale_mtime: path_mtime,
2056            });
2057        }
2058    }
2059
2060    debug!(
2061        "all paths up-to-date relative to {:?} mtime={}",
2062        reference, reference_mtime
2063    );
2064    None
2065}