#
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]: 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//! 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::Context as _;
    387use anyhow::format_err;
    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::Package;
    396use crate::core::compiler::unit_graph::UnitDep;
    397use crate::util;
    398use crate::util::errors::CargoResult;
    399use crate::util::interning::InternedString;
    400use crate::util::{StableHasher, internal, path_args};
    401use crate::{CARGO_ENV, GlobalContext};
    402
    403use super::custom_build::BuildDeps;
    404use super::{BuildContext, BuildRunner, FileFlavor, Job, Unit, Work};
    405
    406pub use self::dep_info::Checksum;
    407pub use self::dep_info::parse_dep_info;
    408pub use self::dep_info::parse_rustc_dep_info;
    409pub use self::dep_info::translate_dep_info;
    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: name.into(),
    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        build_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 = build_root.join(dep_info);
    856                let Some(info) = parse_dep_info(pkg_root, build_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                &build_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        build_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 Ok(mtime) = paths::mtime(output) else {
    1171                // This path failed to report its `mtime`. It probably doesn't
    1172                // exists, so leave ourselves as stale and bail out.
    1173                let item = StaleItem::FailedToReadMetadata(output.clone());
    1174                self.fs_status = FsStatus::StaleItem(item);
    1175                return Ok(());
    1176            };
    1177            assert!(mtimes.insert(output.clone(), mtime).is_none());
    1178        }
    1179
    1180        let opt_max = mtimes.iter().max_by_key(|kv| kv.1);
    1181        let Some((max_path, max_mtime)) = opt_max else {
    1182            // We had no output files. This means we're an overridden build
    1183            // script and we're just always up to date because we aren't
    1184            // watching the filesystem.
    1185            self.fs_status = FsStatus::UpToDate { mtimes };
    1186            return Ok(());
    1187        };
    1188        debug!(
    1189            "max output mtime for {:?} is {:?} {}",
    1190            pkg_root, max_path, max_mtime
    1191        );
    1192
    1193        for dep in self.deps.iter() {
    1194            let dep_mtimes = match &dep.fingerprint.fs_status {
    1195                FsStatus::UpToDate { mtimes } => mtimes,
    1196                // If our dependency is stale, so are we, so bail out.
    1197                FsStatus::Stale
    1198                | FsStatus::StaleItem(_)
    1199                | FsStatus::StaleDependency { .. }
    1200                | FsStatus::StaleDepFingerprint { .. } => {
    1201                    self.fs_status = FsStatus::StaleDepFingerprint { name: dep.name };
    1202                    return Ok(());
    1203                }
    1204            };
    1205
    1206            // If our dependency edge only requires the rmeta file to be present
    1207            // then we only need to look at that one output file, otherwise we
    1208            // need to consider all output files to see if we're out of date.
    1209            let (dep_path, dep_mtime) = if dep.only_requires_rmeta {
    1210                dep_mtimes
    1211                    .iter()
    1212                    .find(|(path, _mtime)| {
    1213                        path.extension().and_then(|s| s.to_str()) == Some("rmeta")
    1214                    })
    1215                    .expect("failed to find rmeta")
    1216            } else {
    1217                match dep_mtimes.iter().max_by_key(|kv| kv.1) {
    1218                    Some(dep_mtime) => dep_mtime,
    1219                    // If our dependencies is up to date and has no filesystem
    1220                    // interactions, then we can move on to the next dependency.
    1221                    None => continue,
    1222                }
    1223            };
    1224            debug!(
    1225                "max dep mtime for {:?} is {:?} {}",
    1226                pkg_root, dep_path, dep_mtime
    1227            );
    1228
    1229            // If the dependency is newer than our own output then it was
    1230            // recompiled previously. We transitively become stale ourselves in
    1231            // that case, so bail out.
    1232            //
    1233            // Note that this comparison should probably be `>=`, not `>`, but
    1234            // for a discussion of why it's `>` see the discussion about #5918
    1235            // below in `find_stale`.
    1236            if dep_mtime > max_mtime {
    1237                info!(
    1238                    "dependency on `{}` is newer than we are {} > {} {:?}",
    1239                    dep.name, dep_mtime, max_mtime, pkg_root
    1240                );
    1241
    1242                self.fs_status = FsStatus::StaleDependency {
    1243                    name: dep.name,
    1244                    dep_mtime: *dep_mtime,
    1245                    max_mtime: *max_mtime,
    1246                };
    1247
    1248                return Ok(());
    1249            }
    1250        }
    1251
    1252        // If we reached this far then all dependencies are up to date. Check
    1253        // all our `LocalFingerprint` information to see if we have any stale
    1254        // files for this package itself. If we do find something log a helpful
    1255        // message and bail out so we stay stale.
    1256        for local in self.local.get_mut().unwrap().iter() {
    1257            if let Some(item) = local.find_stale_item(
    1258                mtime_cache,
    1259                checksum_cache,
    1260                pkg,
    1261                build_root,
    1262                cargo_exe,
    1263                gctx,
    1264            )? {
    1265                item.log();
    1266                self.fs_status = FsStatus::StaleItem(item);
    1267                return Ok(());
    1268            }
    1269        }
    1270
    1271        // Everything was up to date! Record such.
    1272        self.fs_status = FsStatus::UpToDate { mtimes };
    1273        debug!("filesystem up-to-date {:?}", pkg_root);
    1274
    1275        Ok(())
    1276    }
    1277}
    1278
    1279impl hash::Hash for Fingerprint {
    1280    fn hash<H: Hasher>(&self, h: &mut H) {
    1281        let Fingerprint {
    1282            rustc,
    1283            ref features,
    1284            ref declared_features,
    1285            target,
    1286            path,
    1287            profile,
    1288            ref deps,
    1289            ref local,
    1290            config,
    1291            compile_kind,
    1292            ref rustflags,
    1293            ..
    1294        } = *self;
    1295        let local = local.lock().unwrap();
    1296        (
    1297            rustc,
    1298            features,
    1299            declared_features,
    1300            target,
    1301            path,
    1302            profile,
    1303            &*local,
    1304            config,
    1305            compile_kind,
    1306            rustflags,
    1307        )
    1308            .hash(h);
    1309
    1310        h.write_usize(deps.len());
    1311        for DepFingerprint {
    1312            pkg_id,
    1313            name,
    1314            public,
    1315            fingerprint,
    1316            only_requires_rmeta: _, // static property, no need to hash
    1317        } in deps
    1318        {
    1319            pkg_id.hash(h);
    1320            name.hash(h);
    1321            public.hash(h);
    1322            // use memoized dep hashes to avoid exponential blowup
    1323            h.write_u64(fingerprint.hash_u64());
    1324        }
    1325    }
    1326}
    1327
    1328impl DepFingerprint {
    1329    fn new(
    1330        build_runner: &mut BuildRunner<'_, '_>,
    1331        parent: &Unit,
    1332        dep: &UnitDep,
    1333    ) -> CargoResult<DepFingerprint> {
    1334        let fingerprint = calculate(build_runner, &dep.unit)?;
    1335        // We need to be careful about what we hash here. We have a goal of
    1336        // supporting renaming a project directory and not rebuilding
    1337        // everything. To do that, however, we need to make sure that the cwd
    1338        // doesn't make its way into any hashes, and one source of that is the
    1339        // `SourceId` for `path` packages.
    1340        //
    1341        // We already have a requirement that `path` packages all have unique
    1342        // names (sort of for this same reason), so if the package source is a
    1343        // `path` then we just hash the name, but otherwise we hash the full
    1344        // id as it won't change when the directory is renamed.
    1345        let pkg_id = if dep.unit.pkg.package_id().source_id().is_path() {
    1346            util::hash_u64(dep.unit.pkg.package_id().name())
    1347        } else {
    1348            util::hash_u64(dep.unit.pkg.package_id())
    1349        };
    1350
    1351        Ok(DepFingerprint {
    1352            pkg_id,
    1353            name: dep.extern_crate_name,
    1354            public: dep.public,
    1355            fingerprint,
    1356            only_requires_rmeta: build_runner.only_requires_rmeta(parent, &dep.unit),
    1357        })
    1358    }
    1359}
    1360
    1361impl StaleItem {
    1362    /// Use the `log` crate to log a hopefully helpful message in diagnosing
    1363    /// what file is considered stale and why. This is intended to be used in
    1364    /// conjunction with `CARGO_LOG` to determine why Cargo is recompiling
    1365    /// something. Currently there's no user-facing usage of this other than
    1366    /// that.
    1367    fn log(&self) {
    1368        match self {
    1369            StaleItem::MissingFile(path) => {
    1370                info!("stale: missing {:?}", path);
    1371            }
    1372            StaleItem::UnableToReadFile(path) => {
    1373                info!("stale: unable to read {:?}", path);
    1374            }
    1375            StaleItem::FailedToReadMetadata(path) => {
    1376                info!("stale: couldn't read metadata {:?}", path);
    1377            }
    1378            StaleItem::ChangedFile {
    1379                reference,
    1380                reference_mtime,
    1381                stale,
    1382                stale_mtime,
    1383            } => {
    1384                info!("stale: changed {:?}", stale);
    1385                info!("          (vs) {:?}", reference);
    1386                info!("               {:?} < {:?}", reference_mtime, stale_mtime);
    1387            }
    1388            StaleItem::FileSizeChanged {
    1389                path,
    1390                new_size,
    1391                old_size,
    1392            } => {
    1393                info!("stale: changed {:?}", path);
    1394                info!("prior file size {old_size}");
    1395                info!("  new file size {new_size}");
    1396            }
    1397            StaleItem::ChangedChecksum {
    1398                source,
    1399                stored_checksum,
    1400                new_checksum,
    1401            } => {
    1402                info!("stale: changed {:?}", source);
    1403                info!("prior checksum {stored_checksum}");
    1404                info!("  new checksum {new_checksum}");
    1405            }
    1406            StaleItem::MissingChecksum(path) => {
    1407                info!("stale: no prior checksum {:?}", path);
    1408            }
    1409            StaleItem::ChangedEnv {
    1410                var,
    1411                previous,
    1412                current,
    1413            } => {
    1414                info!("stale: changed env {:?}", var);
    1415                info!("       {:?} != {:?}", previous, current);
    1416            }
    1417        }
    1418    }
    1419}
    1420
    1421/// Calculates the fingerprint for a [`Unit`].
    1422///
    1423/// This fingerprint is used by Cargo to learn about when information such as:
    1424///
    1425/// * A non-path package changes (changes version, changes revision, etc).
    1426/// * Any dependency changes
    1427/// * The compiler changes
    1428/// * The set of features a package is built with changes
    1429/// * The profile a target is compiled with changes (e.g., opt-level changes)
    1430/// * Any other compiler flags change that will affect the result
    1431///
    1432/// Information like file modification time is only calculated for path
    1433/// dependencies.
    1434fn calculate(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Arc<Fingerprint>> {
    1435    // This function is slammed quite a lot, so the result is memoized.
    1436    if let Some(s) = build_runner.fingerprints.get(unit) {
    1437        return Ok(Arc::clone(s));
    1438    }
    1439    let mut fingerprint = if unit.mode.is_run_custom_build() {
    1440        calculate_run_custom_build(build_runner, unit)?
    1441    } else if unit.mode.is_doc_test() {
    1442        panic!("doc tests do not fingerprint");
    1443    } else {
    1444        calculate_normal(build_runner, unit)?
    1445    };
    1446
    1447    // After we built the initial `Fingerprint` be sure to update the
    1448    // `fs_status` field of it.
    1449    let build_root = build_root(build_runner);
    1450    let cargo_exe = build_runner.bcx.gctx.cargo_exe()?;
    1451    fingerprint.check_filesystem(
    1452        &mut build_runner.mtime_cache,
    1453        &mut build_runner.checksum_cache,
    1454        &unit.pkg,
    1455        &build_root,
    1456        cargo_exe,
    1457        build_runner.bcx.gctx,
    1458    )?;
    1459
    1460    let fingerprint = Arc::new(fingerprint);
    1461    build_runner
    1462        .fingerprints
    1463        .insert(unit.clone(), Arc::clone(&fingerprint));
    1464    Ok(fingerprint)
    1465}
    1466
    1467/// Calculate a fingerprint for a "normal" unit, or anything that's not a build
    1468/// script. This is an internal helper of [`calculate`], don't call directly.
    1469fn calculate_normal(
    1470    build_runner: &mut BuildRunner<'_, '_>,
    1471    unit: &Unit,
    1472) -> CargoResult<Fingerprint> {
    1473    let deps = {
    1474        // Recursively calculate the fingerprint for all of our dependencies.
    1475        //
    1476        // Skip fingerprints of binaries because they don't actually induce a
    1477        // recompile, they're just dependencies in the sense that they need to be
    1478        // built. The only exception here are artifact dependencies,
    1479        // which is an actual dependency that needs a recompile.
    1480        //
    1481        // Create Vec since mutable build_runner is needed in closure.
    1482        let deps = Vec::from(build_runner.unit_deps(unit));
    1483        let mut deps = deps
    1484            .into_iter()
    1485            .filter(|dep| !dep.unit.target.is_bin() || dep.unit.artifact.is_true())
    1486            .map(|dep| DepFingerprint::new(build_runner, unit, &dep))
    1487            .collect::<CargoResult<Vec<_>>>()?;
    1488        deps.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id));
    1489        deps
    1490    };
    1491
    1492    // Afterwards calculate our own fingerprint information.
    1493    let build_root = build_root(build_runner);
    1494    let is_any_doc_gen = unit.mode.is_doc() || unit.mode.is_doc_scrape();
    1495    let rustdoc_depinfo_enabled = build_runner.bcx.gctx.cli_unstable().rustdoc_depinfo;
    1496    let local = if is_any_doc_gen && !rustdoc_depinfo_enabled {
    1497        // rustdoc does not have dep-info files.
    1498        let fingerprint = pkg_fingerprint(build_runner.bcx, &unit.pkg).with_context(|| {
    1499            format!(
    1500                "failed to determine package fingerprint for documenting {}",
    1501                unit.pkg
    1502            )
    1503        })?;
    1504        vec![LocalFingerprint::Precalculated(fingerprint)]
    1505    } else {
    1506        let dep_info = dep_info_loc(build_runner, unit);
    1507        let dep_info = dep_info.strip_prefix(&build_root).unwrap().to_path_buf();
    1508        vec![LocalFingerprint::CheckDepInfo {
    1509            dep_info,
    1510            checksum: build_runner.bcx.gctx.cli_unstable().checksum_freshness,
    1511        }]
    1512    };
    1513
    1514    // Figure out what the outputs of our unit is, and we'll be storing them
    1515    // into the fingerprint as well.
    1516    let outputs = build_runner
    1517        .outputs(unit)?
    1518        .iter()
    1519        .filter(|output| {
    1520            !matches!(
    1521                output.flavor,
    1522                FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
    1523            )
    1524        })
    1525        .map(|output| output.path.clone())
    1526        .collect();
    1527
    1528    // Fill out a bunch more information that we'll be tracking typically
    1529    // hashed to take up less space on disk as we just need to know when things
    1530    // change.
    1531    let extra_flags = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
    1532        &unit.rustdocflags
    1533    } else {
    1534        &unit.rustflags
    1535    }
    1536    .to_vec();
    1537
    1538    let profile_hash = util::hash_u64((
    1539        &unit.profile,
    1540        unit.mode,
    1541        build_runner.bcx.extra_args_for(unit),
    1542        build_runner.lto[unit],
    1543        unit.pkg.manifest().lint_rustflags(),
    1544    ));
    1545    let mut config = StableHasher::new();
    1546    if let Some(linker) = build_runner.compilation.target_linker(unit.kind) {
    1547        linker.hash(&mut config);
    1548    }
    1549    if unit.mode.is_doc() && build_runner.bcx.gctx.cli_unstable().rustdoc_map {
    1550        if let Ok(map) = build_runner.bcx.gctx.doc_extern_map() {
    1551            map.hash(&mut config);
    1552        }
    1553    }
    1554    if let Some(allow_features) = &build_runner.bcx.gctx.cli_unstable().allow_features {
    1555        allow_features.hash(&mut config);
    1556    }
    1557    let compile_kind = unit.kind.fingerprint_hash();
    1558    let mut declared_features = unit.pkg.summary().features().keys().collect::<Vec<_>>();
    1559    declared_features.sort(); // to avoid useless rebuild if the user orders it's features
    1560    // differently
    1561    Ok(Fingerprint {
    1562        rustc: util::hash_u64(&build_runner.bcx.rustc().verbose_version),
    1563        target: util::hash_u64(&unit.target),
    1564        profile: profile_hash,
    1565        // Note that .0 is hashed here, not .1 which is the cwd. That doesn't
    1566        // actually affect the output artifact so there's no need to hash it.
    1567        path: util::hash_u64(path_args(build_runner.bcx.ws, unit).0),
    1568        features: format!("{:?}", unit.features),
    1569        declared_features: format!("{declared_features:?}"),
    1570        deps,
    1571        local: Mutex::new(local),
    1572        memoized_hash: Mutex::new(None),
    1573        config: Hasher::finish(&config),
    1574        compile_kind,
    1575        rustflags: extra_flags,
    1576        fs_status: FsStatus::Stale,
    1577        outputs,
    1578    })
    1579}
    1580
    1581/// Calculate a fingerprint for an "execute a build script" unit.  This is an
    1582/// internal helper of [`calculate`], don't call directly.
    1583fn calculate_run_custom_build(
    1584    build_runner: &mut BuildRunner<'_, '_>,
    1585    unit: &Unit,
    1586) -> CargoResult<Fingerprint> {
    1587    assert!(unit.mode.is_run_custom_build());
    1588    // Using the `BuildDeps` information we'll have previously parsed and
    1589    // inserted into `build_explicit_deps` built an initial snapshot of the
    1590    // `LocalFingerprint` list for this build script. If we previously executed
    1591    // the build script this means we'll be watching files and env vars.
    1592    // Otherwise if we haven't previously executed it we'll just start watching
    1593    // the whole crate.
    1594    let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?;
    1595    let deps = &build_runner.build_explicit_deps[unit];
    1596    let local = (gen_local)(
    1597        deps,
    1598        Some(&|| {
    1599            const IO_ERR_MESSAGE: &str = "\
    1600An I/O error happened. Please make sure you can access the file.
    1601
    1602By default, if your project contains a build script, cargo scans all files in
    1603it to determine whether a rebuild is needed. If you don't expect to access the
    1604file, specify `rerun-if-changed` in your build script.
    1605See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed for more information.";
    1606            pkg_fingerprint(build_runner.bcx, &unit.pkg).map_err(|err| {
    1607                let mut message = format!("failed to determine package fingerprint for build script for {}", unit.pkg);
    1608                if err.root_cause().is::<io::Error>() {
    1609                    message = format!("{}\n{}", message, IO_ERR_MESSAGE)
    1610                }
    1611                err.context(message)
    1612            })
    1613        }),
    1614    )?
    1615    .unwrap();
    1616    let output = deps.build_script_output.clone();
    1617
    1618    // Include any dependencies of our execution, which is typically just the
    1619    // compilation of the build script itself. (if the build script changes we
    1620    // should be rerun!). Note though that if we're an overridden build script
    1621    // we have no dependencies so no need to recurse in that case.
    1622    let deps = if overridden {
    1623        // Overridden build scripts don't need to track deps.
    1624        vec![]
    1625    } else {
    1626        // Create Vec since mutable build_runner is needed in closure.
    1627        let deps = Vec::from(build_runner.unit_deps(unit));
    1628        deps.into_iter()
    1629            .map(|dep| DepFingerprint::new(build_runner, unit, &dep))
    1630            .collect::<CargoResult<Vec<_>>>()?
    1631    };
    1632
    1633    let rustflags = unit.rustflags.to_vec();
    1634
    1635    Ok(Fingerprint {
    1636        local: Mutex::new(local),
    1637        rustc: util::hash_u64(&build_runner.bcx.rustc().verbose_version),
    1638        deps,
    1639        outputs: if overridden { Vec::new() } else { vec![output] },
    1640        rustflags,
    1641
    1642        // Most of the other info is blank here as we don't really include it
    1643        // in the execution of the build script, but... this may be a latent
    1644        // bug in Cargo.
    1645        ..Fingerprint::new()
    1646    })
    1647}
    1648
    1649/// Get ready to compute the [`LocalFingerprint`] values
    1650/// for a [`RunCustomBuild`] unit.
    1651///
    1652/// This function has, what's on the surface, a seriously wonky interface.
    1653/// You'll call this function and it'll return a closure and a boolean. The
    1654/// boolean is pretty simple in that it indicates whether the `unit` has been
    1655/// overridden via `.cargo/config.toml`. The closure is much more complicated.
    1656///
    1657/// This closure is intended to capture any local state necessary to compute
    1658/// the `LocalFingerprint` values for this unit. It is `Send` and `'static` to
    1659/// be sent to other threads as well (such as when we're executing build
    1660/// scripts). That deduplication is the rationale for the closure at least.
    1661///
    1662/// The arguments to the closure are a bit weirder, though, and I'll apologize
    1663/// in advance for the weirdness too. The first argument to the closure is a
    1664/// `&BuildDeps`. This is the parsed version of a build script, and when Cargo
    1665/// starts up this is cached from previous runs of a build script.  After a
    1666/// build script executes the output file is reparsed and passed in here.
    1667///
    1668/// The second argument is the weirdest, it's *optionally* a closure to
    1669/// call [`pkg_fingerprint`]. The `pkg_fingerprint` requires access to
    1670/// "source map" located in `Context`. That's very non-`'static` and
    1671/// non-`Send`, so it can't be used on other threads, such as when we invoke
    1672/// this after a build script has finished. The `Option` allows us to for sure
    1673/// calculate it on the main thread at the beginning, and then swallow the bug
    1674/// for now where a worker thread after a build script has finished doesn't
    1675/// have access. Ideally there would be no second argument or it would be more
    1676/// "first class" and not an `Option` but something that can be sent between
    1677/// threads. In any case, it's a bug for now.
    1678///
    1679/// This isn't the greatest of interfaces, and if there's suggestions to
    1680/// improve please do so!
    1681///
    1682/// FIXME(#6779) - see all the words above
    1683///
    1684/// [`RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild
    1685fn build_script_local_fingerprints(
    1686    build_runner: &mut BuildRunner<'_, '_>,
    1687    unit: &Unit,
    1688) -> CargoResult<(
    1689    Box<
    1690        dyn FnOnce(
    1691                &BuildDeps,
    1692                Option<&dyn Fn() -> CargoResult<String>>,
    1693            ) -> CargoResult<Option<Vec<LocalFingerprint>>>
    1694            + Send,
    1695    >,
    1696    bool,
    1697)> {
    1698    assert!(unit.mode.is_run_custom_build());
    1699    // First up, if this build script is entirely overridden, then we just
    1700    // return the hash of what we overrode it with. This is the easy case!
    1701    if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) {
    1702        debug!("override local fingerprints deps {}", unit.pkg);
    1703        return Ok((
    1704            Box::new(
    1705                move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult<String>>| {
    1706                    Ok(Some(vec![fingerprint]))
    1707                },
    1708            ),
    1709            true, // this is an overridden build script
    1710        ));
    1711    }
    1712
    1713    // ... Otherwise this is a "real" build script and we need to return a real
    1714    // closure. Our returned closure classifies the build script based on
    1715    // whether it prints `rerun-if-*`. If it *doesn't* print this it's where the
    1716    // magical second argument comes into play, which fingerprints a whole
    1717    // package. Remember that the fact that this is an `Option` is a bug, but a
    1718    // longstanding bug, in Cargo. Recent refactorings just made it painfully
    1719    // obvious.
    1720    let pkg_root = unit.pkg.root().to_path_buf();
    1721    let build_dir = build_root(build_runner);
    1722    let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?);
    1723    let calculate =
    1724        move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult<String>>| {
    1725            if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() {
    1726                match pkg_fingerprint {
    1727                    // FIXME: this is somewhat buggy with respect to docker and
    1728                    // weird filesystems. The `Precalculated` variant
    1729                    // constructed below will, for `path` dependencies, contain
    1730                    // a stringified version of the mtime for the local crate.
    1731                    // This violates one of the things we describe in this
    1732                    // module's doc comment, never hashing mtimes. We should
    1733                    // figure out a better scheme where a package fingerprint
    1734                    // may be a string (like for a registry) or a list of files
    1735                    // (like for a path dependency). Those list of files would
    1736                    // be stored here rather than the mtime of them.
    1737                    Some(f) => {
    1738                        let s = f()?;
    1739                        debug!(
    1740                            "old local fingerprints deps {:?} precalculated={:?}",
    1741                            pkg_root, s
    1742                        );
    1743                        return Ok(Some(vec![LocalFingerprint::Precalculated(s)]));
    1744                    }
    1745                    None => return Ok(None),
    1746                }
    1747            }
    1748
    1749            // Ok so now we're in "new mode" where we can have files listed as
    1750            // dependencies as well as env vars listed as dependencies. Process
    1751            // them all here.
    1752            Ok(Some(local_fingerprints_deps(
    1753                deps,
    1754                &build_dir,
    1755                &pkg_root,
    1756                &env_config,
    1757            )))
    1758        };
    1759
    1760    // Note that `false` == "not overridden"
    1761    Ok((Box::new(calculate), false))
    1762}
    1763
    1764/// Create a [`LocalFingerprint`] for an overridden build script.
    1765/// Returns None if it is not overridden.
    1766fn build_script_override_fingerprint(
    1767    build_runner: &mut BuildRunner<'_, '_>,
    1768    unit: &Unit,
    1769) -> Option<LocalFingerprint> {
    1770    // Build script output is only populated at this stage when it is
    1771    // overridden.
    1772    let build_script_outputs = build_runner.build_script_outputs.lock().unwrap();
    1773    let metadata = build_runner.get_run_build_script_metadata(unit);
    1774    // Returns None if it is not overridden.
    1775    let output = build_script_outputs.get(metadata)?;
    1776    let s = format!(
    1777        "overridden build state with hash: {}",
    1778        util::hash_u64(output)
    1779    );
    1780    Some(LocalFingerprint::Precalculated(s))
    1781}
    1782
    1783/// Compute the [`LocalFingerprint`] values for a [`RunCustomBuild`] unit for
    1784/// non-overridden new-style build scripts only. This is only used when `deps`
    1785/// is already known to have a nonempty `rerun-if-*` somewhere.
    1786///
    1787/// [`RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild
    1788fn local_fingerprints_deps(
    1789    deps: &BuildDeps,
    1790    build_root: &Path,
    1791    pkg_root: &Path,
    1792    env_config: &Arc<HashMap<String, OsString>>,
    1793) -> Vec<LocalFingerprint> {
    1794    debug!("new local fingerprints deps {:?}", pkg_root);
    1795    let mut local = Vec::new();
    1796
    1797    if !deps.rerun_if_changed.is_empty() {
    1798        // Note that like the module comment above says we are careful to never
    1799        // store an absolute path in `LocalFingerprint`, so ensure that we strip
    1800        // absolute prefixes from them.
    1801        let output = deps
    1802            .build_script_output
    1803            .strip_prefix(build_root)
    1804            .unwrap()
    1805            .to_path_buf();
    1806        let paths = deps
    1807            .rerun_if_changed
    1808            .iter()
    1809            .map(|p| p.strip_prefix(pkg_root).unwrap_or(p).to_path_buf())
    1810            .collect();
    1811        local.push(LocalFingerprint::RerunIfChanged { output, paths });
    1812    }
    1813
    1814    local.extend(
    1815        deps.rerun_if_env_changed
    1816            .iter()
    1817            .map(|s| LocalFingerprint::from_env(s, env_config)),
    1818    );
    1819
    1820    local
    1821}
    1822
    1823/// Writes the short fingerprint hash value to `<loc>`
    1824/// and logs detailed JSON information to `<loc>.json`.
    1825fn write_fingerprint(loc: &Path, fingerprint: &Fingerprint) -> CargoResult<()> {
    1826    debug_assert_ne!(fingerprint.rustc, 0);
    1827    // fingerprint::new().rustc == 0, make sure it doesn't make it to the file system.
    1828    // This is mostly so outside tools can reliably find out what rust version this file is for,
    1829    // as we can use the full hash.
    1830    let hash = fingerprint.hash_u64();
    1831    debug!("write fingerprint ({:x}) : {}", hash, loc.display());
    1832    paths::write(loc, util::to_hex(hash).as_bytes())?;
    1833
    1834    let json = serde_json::to_string(fingerprint).unwrap();
    1835    if cfg!(debug_assertions) {
    1836        let f: Fingerprint = serde_json::from_str(&json).unwrap();
    1837        assert_eq!(f.hash_u64(), hash);
    1838    }
    1839    paths::write(&loc.with_extension("json"), json.as_bytes())?;
    1840    Ok(())
    1841}
    1842
    1843/// Prepare for work when a package starts to build
    1844pub fn prepare_init(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
    1845    let new1 = build_runner.files().fingerprint_dir(unit);
    1846
    1847    // Doc tests have no output, thus no fingerprint.
    1848    if !new1.exists() && !unit.mode.is_doc_test() {
    1849        paths::create_dir_all(&new1)?;
    1850    }
    1851
    1852    Ok(())
    1853}
    1854
    1855/// Returns the location that the dep-info file will show up at
    1856/// for the [`Unit`] specified.
    1857pub fn dep_info_loc(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> PathBuf {
    1858    build_runner.files().fingerprint_file_path(unit, "dep-")
    1859}
    1860
    1861/// Returns an absolute path that build directory.
    1862/// All paths are rewritten to be relative to this.
    1863fn build_root(build_runner: &BuildRunner<'_, '_>) -> PathBuf {
    1864    build_runner.bcx.ws.build_dir().into_path_unlocked()
    1865}
    1866
    1867/// Reads the value from the old fingerprint hash file and compare.
    1868///
    1869/// If dirty, it then restores the detailed information
    1870/// from the fingerprint JSON file, and provides an rich dirty reason.
    1871fn compare_old_fingerprint(
    1872    unit: &Unit,
    1873    old_hash_path: &Path,
    1874    new_fingerprint: &Fingerprint,
    1875    mtime_on_use: bool,
    1876    forced: bool,
    1877) -> Option<DirtyReason> {
    1878    if mtime_on_use {
    1879        // update the mtime so other cleaners know we used it
    1880        let t = FileTime::from_system_time(SystemTime::now());
    1881        debug!("mtime-on-use forcing {:?} to {}", old_hash_path, t);
    1882        paths::set_file_time_no_err(old_hash_path, t);
    1883    }
    1884
    1885    let compare = _compare_old_fingerprint(old_hash_path, new_fingerprint);
    1886
    1887    match compare.as_ref() {
    1888        Ok(None) => {}
    1889        Ok(Some(reason)) => {
    1890            info!(
    1891                "fingerprint dirty for {}/{:?}/{:?}",
    1892                unit.pkg, unit.mode, unit.target,
    1893            );
    1894            info!("    dirty: {reason:?}");
    1895        }
    1896        Err(e) => {
    1897            info!(
    1898                "fingerprint error for {}/{:?}/{:?}",
    1899                unit.pkg, unit.mode, unit.target,
    1900            );
    1901            info!("    err: {e:?}");
    1902        }
    1903    }
    1904
    1905    match compare {
    1906        Ok(None) if forced => Some(DirtyReason::Forced),
    1907        Ok(reason) => reason,
    1908        Err(_) => Some(DirtyReason::FreshBuild),
    1909    }
    1910}
    1911
    1912fn _compare_old_fingerprint(
    1913    old_hash_path: &Path,
    1914    new_fingerprint: &Fingerprint,
    1915) -> CargoResult<Option<DirtyReason>> {
    1916    let old_fingerprint_short = paths::read(old_hash_path)?;
    1917
    1918    let new_hash = new_fingerprint.hash_u64();
    1919
    1920    if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() {
    1921        return Ok(None);
    1922    }
    1923
    1924    let old_fingerprint_json = paths::read(&old_hash_path.with_extension("json"))?;
    1925    let old_fingerprint: Fingerprint = serde_json::from_str(&old_fingerprint_json)
    1926        .with_context(|| internal("failed to deserialize json"))?;
    1927    // Fingerprint can be empty after a failed rebuild (see comment in prepare_target).
    1928    if !old_fingerprint_short.is_empty() {
    1929        debug_assert_eq!(
    1930            util::to_hex(old_fingerprint.hash_u64()),
    1931            old_fingerprint_short
    1932        );
    1933    }
    1934
    1935    Ok(Some(new_fingerprint.compare(&old_fingerprint)))
    1936}
    1937
    1938/// Calculates the fingerprint of a unit thats contains no dep-info files.
    1939fn pkg_fingerprint(bcx: &BuildContext<'_, '_>, pkg: &Package) -> CargoResult<String> {
    1940    let source_id = pkg.package_id().source_id();
    1941    let sources = bcx.packages.sources();
    1942
    1943    let source = sources
    1944        .get(source_id)
    1945        .ok_or_else(|| internal("missing package source"))?;
    1946    source.fingerprint(pkg)
    1947}
    1948
    1949/// The `reference` file is considered as "stale" if any file from `paths` has a newer mtime.
    1950fn find_stale_file<I, P>(
    1951    mtime_cache: &mut HashMap<PathBuf, FileTime>,
    1952    checksum_cache: &mut HashMap<PathBuf, Checksum>,
    1953    reference: &Path,
    1954    paths: I,
    1955    use_checksums: bool,
    1956) -> Option<StaleItem>
    1957where
    1958    I: IntoIterator<Item = (P, Option<(u64, Checksum)>)>,
    1959    P: AsRef<Path>,
    1960{
    1961    let Ok(reference_mtime) = paths::mtime(reference) else {
    1962        return Some(StaleItem::MissingFile(reference.to_path_buf()));
    1963    };
    1964
    1965    let skippable_dirs = if let Ok(cargo_home) = home::cargo_home() {
    1966        let skippable_dirs: Vec<_> = ["git", "registry"]
    1967            .into_iter()
    1968            .map(|subfolder| cargo_home.join(subfolder))
    1969            .collect();
    1970        Some(skippable_dirs)
    1971    } else {
    1972        None
    1973    };
    1974    for (path, prior_checksum) in paths {
    1975        let path = path.as_ref();
    1976
    1977        // Assuming anything in cargo_home/{git, registry} is immutable
    1978        // (see also #9455 about marking the src directory readonly) which avoids rebuilds when CI
    1979        // caches $CARGO_HOME/registry/{index, cache} and $CARGO_HOME/git/db across runs, keeping
    1980        // the content the same but changing the mtime.
    1981        if let Some(ref skippable_dirs) = skippable_dirs {
    1982            if skippable_dirs.iter().any(|dir| path.starts_with(dir)) {
    1983                continue;
    1984            }
    1985        }
    1986        if use_checksums {
    1987            let Some((file_len, prior_checksum)) = prior_checksum else {
    1988                return Some(StaleItem::MissingChecksum(path.to_path_buf()));
    1989            };
    1990            let path_buf = path.to_path_buf();
    1991
    1992            let path_checksum = match checksum_cache.entry(path_buf) {
    1993                Entry::Occupied(o) => *o.get(),
    1994                Entry::Vacant(v) => {
    1995                    let Ok(current_file_len) = fs::metadata(&path).map(|m| m.len()) else {
    1996                        return Some(StaleItem::FailedToReadMetadata(path.to_path_buf()));
    1997                    };
    1998                    if current_file_len != file_len {
    1999                        return Some(StaleItem::FileSizeChanged {
    2000                            path: path.to_path_buf(),
    2001                            new_size: current_file_len,
    2002                            old_size: file_len,
    2003                        });
    2004                    }
    2005                    let Ok(file) = File::open(path) else {
    2006                        return Some(StaleItem::MissingFile(path.to_path_buf()));
    2007                    };
    2008                    let Ok(checksum) = Checksum::compute(prior_checksum.algo(), file) else {
    2009                        return Some(StaleItem::UnableToReadFile(path.to_path_buf()));
    2010                    };
    2011                    *v.insert(checksum)
    2012                }
    2013            };
    2014            if path_checksum == prior_checksum {
    2015                continue;
    2016            }
    2017            return Some(StaleItem::ChangedChecksum {
    2018                source: path.to_path_buf(),
    2019                stored_checksum: prior_checksum,
    2020                new_checksum: path_checksum,
    2021            });
    2022        } else {
    2023            let path_mtime = match mtime_cache.entry(path.to_path_buf()) {
    2024                Entry::Occupied(o) => *o.get(),
    2025                Entry::Vacant(v) => {
    2026                    let Ok(mtime) = paths::mtime_recursive(path) else {
    2027                        return Some(StaleItem::MissingFile(path.to_path_buf()));
    2028                    };
    2029                    *v.insert(mtime)
    2030                }
    2031            };
    2032
    2033            // TODO: fix #5918.
    2034            // Note that equal mtimes should be considered "stale". For filesystems with
    2035            // not much timestamp precision like 1s this is would be a conservative approximation
    2036            // to handle the case where a file is modified within the same second after
    2037            // a build starts. We want to make sure that incremental rebuilds pick that up!
    2038            //
    2039            // For filesystems with nanosecond precision it's been seen in the wild that
    2040            // its "nanosecond precision" isn't really nanosecond-accurate. It turns out that
    2041            // kernels may cache the current time so files created at different times actually
    2042            // list the same nanosecond precision. Some digging on #5919 picked up that the
    2043            // kernel caches the current time between timer ticks, which could mean that if
    2044            // a file is updated at most 10ms after a build starts then Cargo may not
    2045            // pick up the build changes.
    2046            //
    2047            // All in all, an equality check here would be a conservative assumption that,
    2048            // if equal, files were changed just after a previous build finished.
    2049            // Unfortunately this became problematic when (in #6484) cargo switch to more accurately
    2050            // measuring the start time of builds.
    2051            if path_mtime <= reference_mtime {
    2052                continue;
    2053            }
    2054
    2055            return Some(StaleItem::ChangedFile {
    2056                reference: reference.to_path_buf(),
    2057                reference_mtime,
    2058                stale: path.to_path_buf(),
    2059                stale_mtime: path_mtime,
    2060            });
    2061        }
    2062    }
    2063
    2064    debug!(
    2065        "all paths up-to-date relative to {:?} mtime={}",
    2066        reference, reference_mtime
    2067    );
    2068    None
    2069}