#
cargo/ops/
resolve.rs

    1//! High-level APIs for executing the resolver.
    2//!
    3//! This module provides functions for running the resolver given a workspace, including loading
    4//! the `Cargo.lock` file and checking if it needs updating.
    5//!
    6//! There are roughly 3 main functions:
    7//!
    8//! - [`resolve_ws`]: A simple, high-level function with no options.
    9//! - [`resolve_ws_with_opts`]: A medium-level function with options like
    10//!   user-provided features. This is the most appropriate function to use in
    11//!   most cases.
    12//! - [`resolve_with_previous`]: A low-level function for running the resolver,
    13//!   providing the most power and flexibility.
    14//!
    15//! ### Data Structures
    16//!
    17//! - [`Workspace`]:
    18//!   Usually created by [`crate::util::command_prelude::ArgMatchesExt::workspace`] which discovers the root of the
    19//!   workspace, and loads all the workspace members as a [`Package`] object
    20//!   - [`Package`]
    21//!     Corresponds with `Cargo.toml` manifest (deserialized as [`Manifest`]) and its associated files.
    22//!     - [`Target`]s are crates such as the library, binaries, integration test, or examples.
    23//!       They are what is actually compiled by `rustc`.
    24//!       Each `Target` defines a crate root, like `src/lib.rs` or `examples/foo.rs`.
    25//!     - [`PackageId`] --- A unique identifier for a package.
    26//! - [`PackageRegistry`]:
    27//!   The primary interface for how the dependency
    28//!   resolver finds packages. It contains the `SourceMap`, and handles things
    29//!   like the `[patch]` table. The dependency resolver
    30//!   sends a query to the `PackageRegistry` to "get me all packages that match
    31//!   this dependency declaration". The `Registry` trait provides a generic interface
    32//!   to the `PackageRegistry`, but this is only used for providing an alternate
    33//!   implementation of the `PackageRegistry` for testing.
    34//! - [`SourceMap`]: Map of all available sources.
    35//!   - [`Source`]: An abstraction for something that can fetch packages (a remote
    36//!     registry, a git repo, the local filesystem, etc.). Check out the [source
    37//!     implementations] for all the details about registries, indexes, git
    38//!     dependencies, etc.
    39//!       * [`SourceId`]: A unique identifier for a source.
    40//!   - [`Summary`]: A of a [`Manifest`], and is essentially
    41//!     the information that can be found in a registry index. Queries against the
    42//!     `PackageRegistry` yields a `Summary`. The resolver uses the summary
    43//!     information to build the dependency graph.
    44//! - [`PackageSet`] --- Contains all the `Package` objects. This works with the
    45//!   [`Downloads`] struct to coordinate downloading packages. It has a reference
    46//!   to the `SourceMap` to get the `Source` objects which tell the `Downloads`
    47//!   struct which URLs to fetch.
    48//!
    49//! [`Package`]: crate::core::package
    50//! [`Target`]: crate::core::Target
    51//! [`Manifest`]: crate::core::Manifest
    52//! [`Source`]: crate::sources::source::Source
    53//! [`SourceMap`]: crate::sources::source::SourceMap
    54//! [`PackageRegistry`]: crate::core::registry::PackageRegistry
    55//! [source implementations]: crate::sources
    56//! [`Downloads`]: crate::core::package::Downloads
    57
    58use crate::core::Dependency;
    59use crate::core::GitReference;
    60use crate::core::PackageId;
    61use crate::core::PackageIdSpec;
    62use crate::core::PackageIdSpecQuery;
    63use crate::core::PackageSet;
    64use crate::core::SourceId;
    65use crate::core::Workspace;
    66use crate::core::compiler::{CompileKind, RustcTargetData};
    67use crate::core::registry::{LockedPatchDependency, PackageRegistry};
    68use crate::core::resolver::features::{
    69    CliFeatures, FeatureOpts, FeatureResolver, ForceAllTargets, RequestedFeatures, ResolvedFeatures,
    70};
    71use crate::core::resolver::{
    72    self, HasDevUnits, Resolve, ResolveOpts, ResolveVersion, VersionOrdering, VersionPreferences,
    73};
    74use crate::core::summary::Summary;
    75use crate::ops;
    76use crate::sources::RecursivePathSource;
    77use crate::util::CanonicalUrl;
    78use crate::util::cache_lock::CacheLockMode;
    79use crate::util::context::FeatureUnification;
    80use crate::util::errors::CargoResult;
    81use annotate_snippets::Group;
    82use annotate_snippets::Level;
    83use anyhow::Context as _;
    84use cargo_util::paths;
    85use cargo_util_schemas::core::PartialVersion;
    86use std::borrow::Cow;
    87use std::collections::{HashMap, HashSet};
    88use std::rc::Rc;
    89use tracing::{debug, trace};
    90
    91/// Filter for keep using Package ID from previous lockfile.
    92type Keep<'a> = &'a dyn Fn(&PackageId) -> bool;
    93
    94/// Result for `resolve_ws_with_opts`.
    95pub struct WorkspaceResolve<'gctx> {
    96    /// Packages to be downloaded.
    97    pub pkg_set: PackageSet<'gctx>,
    98    /// The resolve for the entire workspace.
    99    ///
    100    /// This may be `None` for things like `cargo install` and `-Zavoid-dev-deps`.
    101    /// This does not include `paths` overrides.
    102    pub workspace_resolve: Option<Resolve>,
    103    /// The narrowed resolve, with the specific features enabled.
    104    pub targeted_resolve: Resolve,
    105    /// Package specs requested for compilation along with specific features enabled. This usually
    106    /// has the length of one but there may be more specs with different features when using the
    107    /// `package` feature resolver.
    108    pub specs_and_features: Vec<SpecsAndResolvedFeatures>,
    109}
    110
    111/// Pair of package specs requested for compilation along with enabled features.
    112pub struct SpecsAndResolvedFeatures {
    113    /// Packages that are supposed to be built.
    114    pub specs: Vec<PackageIdSpec>,
    115    /// The features activated per package.
    116    pub resolved_features: ResolvedFeatures,
    117}
    118
    119const UNUSED_PATCH_WARNING: &str = "\
    120Check that the patched package version and available features are compatible
    121with the dependency requirements. If the patch has a different version from
    122what is locked in the Cargo.lock file, run `cargo update` to use the new
    123version. This may also occur with an optional dependency that is not enabled.";
    124
    125/// Resolves all dependencies for the workspace using the previous
    126/// lock file as a guide if present.
    127///
    128/// This function will also write the result of resolution as a new lock file
    129/// (unless it is an ephemeral workspace such as `cargo install` or `cargo
    130/// package`).
    131///
    132/// This is a simple interface used by commands like `clean`, `fetch`, and
    133/// `package`, which don't specify any options or features.
    134pub fn resolve_ws<'a>(ws: &Workspace<'a>, dry_run: bool) -> CargoResult<(PackageSet<'a>, Resolve)> {
    135    let mut registry = ws.package_registry()?;
    136    let resolve = resolve_with_registry(ws, &mut registry, dry_run)?;
    137    let packages = get_resolved_packages(&resolve, registry)?;
    138    Ok((packages, resolve))
    139}
    140
    141/// Resolves dependencies for some packages of the workspace,
    142/// taking into account `paths` overrides and activated features.
    143///
    144/// This function will also write the result of resolution as a new lock file
    145/// (unless `Workspace::require_optional_deps` is false, such as `cargo
    146/// install` or `-Z avoid-dev-deps`), or it is an ephemeral workspace (`cargo
    147/// install` or `cargo package`).
    148///
    149/// `specs` may be empty, which indicates it should resolve all workspace
    150/// members. In this case, `opts.all_features` must be `true`.
    151pub fn resolve_ws_with_opts<'gctx>(
    152    ws: &Workspace<'gctx>,
    153    target_data: &mut RustcTargetData<'gctx>,
    154    requested_targets: &[CompileKind],
    155    cli_features: &CliFeatures,
    156    specs: &[PackageIdSpec],
    157    has_dev_units: HasDevUnits,
    158    force_all_targets: ForceAllTargets,
    159    dry_run: bool,
    160) -> CargoResult<WorkspaceResolve<'gctx>> {
    161    let feature_unification = ws.resolve_feature_unification();
    162    let individual_specs = match feature_unification {
    163        FeatureUnification::Selected => vec![specs.to_owned()],
    164        FeatureUnification::Workspace => {
    165            vec![ops::Packages::All(Vec::new()).to_package_id_specs(ws)?]
    166        }
    167        FeatureUnification::Package => specs.iter().map(|spec| vec![spec.clone()]).collect(),
    168    };
    169    let specs: Vec<_> = individual_specs
    170        .iter()
    171        .map(|specs| specs.iter())
    172        .flatten()
    173        .cloned()
    174        .collect();
    175    let specs = &specs[..];
    176    let mut registry = ws.package_registry()?;
    177    let (resolve, resolved_with_overrides) = if ws.ignore_lock() {
    178        let add_patches = true;
    179        let resolve = None;
    180        let resolved_with_overrides = resolve_with_previous(
    181            &mut registry,
    182            ws,
    183            cli_features,
    184            has_dev_units,
    185            resolve.as_ref(),
    186            None,
    187            specs,
    188            add_patches,
    189        )?;
    190        ops::print_lockfile_changes(ws, None, &resolved_with_overrides, &mut registry)?;
    191        (resolve, resolved_with_overrides)
    192    } else if ws.require_optional_deps() {
    193        // First, resolve the root_package's *listed* dependencies, as well as
    194        // downloading and updating all remotes and such.
    195        let resolve = resolve_with_registry(ws, &mut registry, dry_run)?;
    196        // No need to add patches again, `resolve_with_registry` has done it.
    197        let add_patches = false;
    198
    199        // Second, resolve with precisely what we're doing. Filter out
    200        // transitive dependencies if necessary, specify features, handle
    201        // overrides, etc.
    202        add_overrides(&mut registry, ws)?;
    203
    204        for (replace_spec, dep) in ws.root_replace() {
    205            if !resolve
    206                .iter()
    207                .any(|r| replace_spec.matches(r) && !dep.matches_id(r))
    208            {
    209                ws.gctx()
    210                    .shell()
    211                    .warn(format!("package replacement is not used: {}", replace_spec))?
    212            }
    213
    214            let mut unused_fields = Vec::new();
    215            if dep.features().len() != 0 {
    216                unused_fields.push("`features`");
    217            }
    218            if !dep.uses_default_features() {
    219                unused_fields.push("`default-features`")
    220            }
    221            if !unused_fields.is_empty() {
    222                ws.gctx().shell().print_report(
    223                    &[Level::WARNING
    224                        .secondary_title(format!(
    225                            "unused field in replacement for `{}`: {}",
    226                            dep.package_name(),
    227                            unused_fields.join(", ")
    228                        ))
    229                        .element(Level::NOTE.message(format!(
    230                            "configure {} in the `dependencies` entry",
    231                            unused_fields.join(", ")
    232                        )))],
    233                    false,
    234                )?;
    235            }
    236        }
    237
    238        let resolved_with_overrides = resolve_with_previous(
    239            &mut registry,
    240            ws,
    241            cli_features,
    242            has_dev_units,
    243            Some(&resolve),
    244            None,
    245            specs,
    246            add_patches,
    247        )?;
    248        (Some(resolve), resolved_with_overrides)
    249    } else {
    250        let add_patches = true;
    251        let resolve = ops::load_pkg_lockfile(ws)?;
    252        let resolved_with_overrides = resolve_with_previous(
    253            &mut registry,
    254            ws,
    255            cli_features,
    256            has_dev_units,
    257            resolve.as_ref(),
    258            None,
    259            specs,
    260            add_patches,
    261        )?;
    262        // Skipping `print_lockfile_changes` as there are cases where this prints irrelevant
    263        // information
    264        (resolve, resolved_with_overrides)
    265    };
    266
    267    let pkg_set = get_resolved_packages(&resolved_with_overrides, registry)?;
    268
    269    let members_with_features = ws.members_with_features(specs, cli_features)?;
    270    let member_ids = members_with_features
    271        .iter()
    272        .map(|(p, _fts)| p.package_id())
    273        .collect::<Vec<_>>();
    274    pkg_set.download_accessible(
    275        &resolved_with_overrides,
    276        &member_ids,
    277        has_dev_units,
    278        requested_targets,
    279        target_data,
    280        force_all_targets,
    281    )?;
    282
    283    let mut specs_and_features = Vec::new();
    284
    285    for specs in individual_specs {
    286        let feature_opts = FeatureOpts::new(ws, has_dev_units, force_all_targets)?;
    287
    288        // We want to narrow the features to the current specs so that stuff like `cargo check -p a
    289        // -p b -F a/a,b/b` works and the resolver does not contain that `a` does not have feature
    290        // `b` and vice-versa. However, resolver v1 needs to see even features of unselected
    291        // packages turned on if it was because of working directory being inside the unselected
    292        // package, because they might turn on a feature of a selected package.
    293        let narrowed_features = match feature_unification {
    294            FeatureUnification::Package => {
    295                let mut narrowed_features = cli_features.clone();
    296                let enabled_features = members_with_features
    297                    .iter()
    298                    .filter_map(|(package, cli_features)| {
    299                        specs
    300                            .iter()
    301                            .any(|spec| spec.matches(package.package_id()))
    302                            .then_some(cli_features.features.iter())
    303                    })
    304                    .flatten()
    305                    .cloned()
    306                    .collect();
    307                narrowed_features.features = Rc::new(enabled_features);
    308                Cow::Owned(narrowed_features)
    309            }
    310            FeatureUnification::Selected | FeatureUnification::Workspace => {
    311                Cow::Borrowed(cli_features)
    312            }
    313        };
    314
    315        let resolved_features = FeatureResolver::resolve(
    316            ws,
    317            target_data,
    318            &resolved_with_overrides,
    319            &pkg_set,
    320            &*narrowed_features,
    321            &specs,
    322            requested_targets,
    323            feature_opts,
    324        )?;
    325
    326        pkg_set.warn_no_lib_packages_and_artifact_libs_overlapping_deps(
    327            ws,
    328            &resolved_with_overrides,
    329            &member_ids,
    330            has_dev_units,
    331            requested_targets,
    332            target_data,
    333            force_all_targets,
    334        )?;
    335
    336        specs_and_features.push(SpecsAndResolvedFeatures {
    337            specs,
    338            resolved_features,
    339        });
    340    }
    341
    342    Ok(WorkspaceResolve {
    343        pkg_set,
    344        workspace_resolve: resolve,
    345        targeted_resolve: resolved_with_overrides,
    346        specs_and_features,
    347    })
    348}
    349
    350#[tracing::instrument(skip_all)]
    351fn resolve_with_registry<'gctx>(
    352    ws: &Workspace<'gctx>,
    353    registry: &mut PackageRegistry<'gctx>,
    354    dry_run: bool,
    355) -> CargoResult<Resolve> {
    356    let prev = ops::load_pkg_lockfile(ws)?;
    357    let mut resolve = resolve_with_previous(
    358        registry,
    359        ws,
    360        &CliFeatures::new_all(true),
    361        HasDevUnits::Yes,
    362        prev.as_ref(),
    363        None,
    364        &[],
    365        true,
    366    )?;
    367
    368    let print = if !ws.is_ephemeral() && ws.require_optional_deps() {
    369        if !dry_run {
    370            ops::write_pkg_lockfile(ws, &mut resolve)?
    371        } else {
    372            true
    373        }
    374    } else {
    375        // This mostly represents
    376        // - `cargo install --locked` and the only change is the package is no longer local but
    377        //   from the registry which is noise
    378        // - publish of libraries
    379        false
    380    };
    381    if print {
    382        ops::print_lockfile_changes(ws, prev.as_ref(), &resolve, registry)?;
    383    }
    384    Ok(resolve)
    385}
    386
    387/// Resolves all dependencies for a package using an optional previous instance
    388/// of resolve to guide the resolution process.
    389///
    390/// This also takes an optional filter `keep_previous`, which informs the `registry`
    391/// which package ID should be locked to the previous instance of resolve
    392/// (often used in pairings with updates). See comments in [`register_previous_locks`]
    393/// for scenarios that might override this.
    394///
    395/// The previous resolve normally comes from a lock file. This function does not
    396/// read or write lock files from the filesystem.
    397///
    398/// `specs` may be empty, which indicates it should resolve all workspace
    399/// members. In this case, `opts.all_features` must be `true`.
    400///
    401/// If `register_patches` is true, then entries from the `[patch]` table in
    402/// the manifest will be added to the given `PackageRegistry`.
    403#[tracing::instrument(skip_all)]
    404pub fn resolve_with_previous<'gctx>(
    405    registry: &mut PackageRegistry<'gctx>,
    406    ws: &Workspace<'gctx>,
    407    cli_features: &CliFeatures,
    408    has_dev_units: HasDevUnits,
    409    previous: Option<&Resolve>,
    410    keep_previous: Option<Keep<'_>>,
    411    specs: &[PackageIdSpec],
    412    register_patches: bool,
    413) -> CargoResult<Resolve> {
    414    // We only want one Cargo at a time resolving a crate graph since this can
    415    // involve a lot of frobbing of the global caches.
    416    let _lock = ws
    417        .gctx()
    418        .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
    419
    420    // Some packages are already loaded when setting up a workspace. This
    421    // makes it so anything that was already loaded will not be loaded again.
    422    // Without this there were cases where members would be parsed multiple times
    423    ws.preload(registry);
    424
    425    // In case any members were not already loaded or the Workspace is_ephemeral.
    426    for member in ws.members() {
    427        registry.add_sources(Some(member.package_id().source_id()))?;
    428    }
    429
    430    // Try to keep all from previous resolve if no instruction given.
    431    let keep_previous = keep_previous.unwrap_or(&|_| true);
    432
    433    // While registering patches, we will record preferences for particular versions
    434    // of various packages.
    435    let mut version_prefs = VersionPreferences::default();
    436    if ws.gctx().cli_unstable().minimal_versions {
    437        version_prefs.version_ordering(VersionOrdering::MinimumVersionsFirst)
    438    }
    439    if ws.resolve_honors_rust_version() {
    440        let mut rust_versions: Vec<_> = ws
    441            .members()
    442            .filter_map(|p| p.rust_version().map(|rv| rv.as_partial().clone()))
    443            .collect();
    444        if rust_versions.is_empty() {
    445            let rustc = ws.gctx().load_global_rustc(Some(ws))?;
    446            let rust_version: PartialVersion = rustc.version.clone().into();
    447            rust_versions.push(rust_version);
    448        }
    449        version_prefs.rust_versions(rust_versions);
    450    }
    451    if let Some(publish_time) = ws.resolve_publish_time() {
    452        version_prefs.publish_time(publish_time);
    453    }
    454
    455    let avoid_patch_ids = if register_patches {
    456        register_patch_entries(registry, ws, previous, &mut version_prefs, keep_previous)?
    457    } else {
    458        HashSet::new()
    459    };
    460
    461    // Refine `keep` with patches that should avoid locking.
    462    let keep = |p: &PackageId| keep_previous(p) && !avoid_patch_ids.contains(p);
    463
    464    let dev_deps = ws.require_optional_deps() || has_dev_units == HasDevUnits::Yes;
    465
    466    if let Some(r) = previous {
    467        trace!("previous: {:?}", r);
    468
    469        // In the case where a previous instance of resolve is available, we
    470        // want to lock as many packages as possible to the previous version
    471        // without disturbing the graph structure.
    472        register_previous_locks(ws, registry, r, &keep, dev_deps);
    473
    474        // Prefer to use anything in the previous lock file, aka we want to have conservative updates.
    475        let _span = tracing::span!(tracing::Level::TRACE, "prefer_package_id").entered();
    476        for id in r.iter().filter(keep) {
    477            debug!("attempting to prefer {}", id);
    478            version_prefs.prefer_package_id(id);
    479        }
    480    }
    481
    482    if register_patches {
    483        registry.lock_patches();
    484    }
    485
    486    let summaries: Vec<(Summary, ResolveOpts)> = {
    487        let _span = tracing::span!(tracing::Level::TRACE, "registry.lock").entered();
    488        ws.members_with_features(specs, cli_features)?
    489            .into_iter()
    490            .map(|(member, features)| {
    491                let summary = registry.lock(member.summary().clone());
    492                (
    493                    summary,
    494                    ResolveOpts {
    495                        dev_deps,
    496                        features: RequestedFeatures::CliFeatures(features),
    497                    },
    498                )
    499            })
    500            .collect()
    501    };
    502
    503    let replace = lock_replacements(ws, previous, &keep);
    504
    505    let mut resolved = resolver::resolve(
    506        &summaries,
    507        &replace,
    508        registry,
    509        &version_prefs,
    510        ResolveVersion::with_rust_version(ws.lowest_rust_version()),
    511        Some(ws.gctx()),
    512    )?;
    513
    514    let patches = registry.patches().values().flat_map(|v| v.iter());
    515    resolved.register_used_patches(patches);
    516
    517    if register_patches && !resolved.unused_patches().is_empty() {
    518        emit_warnings_of_unused_patches(ws, &resolved, registry)?;
    519    }
    520
    521    if let Some(previous) = previous {
    522        resolved.merge_from(previous)?;
    523    }
    524    let gctx = ws.gctx();
    525    let mut deferred = gctx.deferred_global_last_use()?;
    526    deferred.save_no_error(gctx);
    527    Ok(resolved)
    528}
    529
    530/// Read the `paths` configuration variable to discover all path overrides that
    531/// have been configured.
    532#[tracing::instrument(skip_all)]
    533pub fn add_overrides<'a>(
    534    registry: &mut PackageRegistry<'a>,
    535    ws: &Workspace<'a>,
    536) -> CargoResult<()> {
    537    let gctx = ws.gctx();
    538    let Some(paths) = gctx.paths_overrides()? else {
    539        return Ok(());
    540    };
    541
    542    let paths = paths.val.iter().map(|(s, def)| {
    543        // The path listed next to the string is the config file in which the
    544        // key was located, so we want to pop off the `.cargo/config` component
    545        // to get the directory containing the `.cargo` folder.
    546        (paths::normalize_path(&def.root(gctx.cwd()).join(s)), def)
    547    });
    548
    549    for (path, definition) in paths {
    550        let id = SourceId::for_path(&path)?;
    551        let mut source = RecursivePathSource::new(&path, id, ws.gctx());
    552        source.load().with_context(|| {
    553            format!(
    554                "failed to update path override `{}` \
    555                 (defined in `{}`)",
    556                path.display(),
    557                definition
    558            )
    559        })?;
    560        registry.add_override(Box::new(source));
    561    }
    562    Ok(())
    563}
    564
    565pub fn get_resolved_packages<'gctx>(
    566    resolve: &Resolve,
    567    registry: PackageRegistry<'gctx>,
    568) -> CargoResult<PackageSet<'gctx>> {
    569    let ids: Vec<PackageId> = resolve.iter().collect();
    570    registry.get(&ids)
    571}
    572
    573/// In this function we're responsible for informing the `registry` of all
    574/// locked dependencies from the previous lock file we had, `resolve`.
    575///
    576/// This gets particularly tricky for a couple of reasons. The first is that we
    577/// want all updates to be conservative, so we actually want to take the
    578/// `resolve` into account (and avoid unnecessary registry updates and such).
    579/// the second, however, is that we want to be resilient to updates of
    580/// manifests. For example if a dependency is added or a version is changed we
    581/// want to make sure that we properly re-resolve (conservatively) instead of
    582/// providing an opaque error.
    583///
    584/// The logic here is somewhat subtle, but there should be more comments below to
    585/// clarify things.
    586///
    587/// Note that this function, at the time of this writing, is basically the
    588/// entire fix for issue #4127.
    589#[tracing::instrument(skip_all)]
    590fn register_previous_locks(
    591    ws: &Workspace<'_>,
    592    registry: &mut PackageRegistry<'_>,
    593    resolve: &Resolve,
    594    keep: Keep<'_>,
    595    dev_deps: bool,
    596) {
    597    let path_pkg = |id: SourceId| {
    598        if !id.is_path() {
    599            return None;
    600        }
    601        if let Ok(path) = id.url().to_file_path() {
    602            if let Ok(pkg) = ws.load(&path.join("Cargo.toml")) {
    603                return Some(pkg);
    604            }
    605        }
    606        None
    607    };
    608
    609    // Ok so we've been passed in a `keep` function which basically says "if I
    610    // return `true` then this package wasn't listed for an update on the command
    611    // line". That is, if we run `cargo update foo` then `keep(bar)` will return
    612    // `true`, whereas `keep(foo)` will return `false` (roughly speaking).
    613    //
    614    // This isn't actually quite what we want, however. Instead we want to
    615    // further refine this `keep` function with *all transitive dependencies* of
    616    // the packages we're not keeping. For example, consider a case like this:
    617    //
    618    // * There's a crate `log`.
    619    // * There's a crate `serde` which depends on `log`.
    620    //
    621    // Let's say we then run `cargo update serde`. This may *also* want to
    622    // update the `log` dependency as our newer version of `serde` may have a
    623    // new minimum version required for `log`. Now this isn't always guaranteed
    624    // to work. What'll happen here is we *won't* lock the `log` dependency nor
    625    // the `log` crate itself, but we will inform the registry "please prefer
    626    // this version of `log`". That way if our newer version of serde works with
    627    // the older version of `log`, we conservatively won't update `log`. If,
    628    // however, nothing else in the dependency graph depends on `log` and the
    629    // newer version of `serde` requires a new version of `log` it'll get pulled
    630    // in (as we didn't accidentally lock it to an old version).
    631    let mut avoid_locking = HashSet::new();
    632    registry.add_to_yanked_whitelist(resolve.iter().filter(keep));
    633    for node in resolve.iter() {
    634        if !keep(&node) {
    635            add_deps(resolve, node, &mut avoid_locking);
    636        }
    637    }
    638
    639    // Ok, but the above loop isn't the entire story! Updates to the dependency
    640    // graph can come from two locations, the `cargo update` command or
    641    // manifests themselves. For example a manifest on the filesystem may
    642    // have been updated to have an updated version requirement on `serde`. In
    643    // this case both `keep(serde)` and `keep(log)` return `true` (the `keep`
    644    // that's an argument to this function). We, however, don't want to keep
    645    // either of those! Otherwise we'll get obscure resolve errors about locked
    646    // versions.
    647    //
    648    // To solve this problem we iterate over all packages with path sources
    649    // (aka ones with manifests that are changing) and take a look at all of
    650    // their dependencies. If any dependency does not match something in the
    651    // previous lock file, then we're guaranteed that the main resolver will
    652    // update the source of this dependency no matter what. Knowing this we
    653    // poison all packages from the same source, forcing them all to get
    654    // updated.
    655    //
    656    // This may seem like a heavy hammer, and it is! It means that if you change
    657    // anything from crates.io then all of crates.io becomes unlocked. Note,
    658    // however, that we still want conservative updates. This currently happens
    659    // because the first candidate the resolver picks is the previously locked
    660    // version, and only if that fails to activate to we move on and try
    661    // a different version. (giving the guise of conservative updates)
    662    //
    663    // For example let's say we had `serde = "0.1"` written in our lock file.
    664    // When we later edit this to `serde = "0.1.3"` we don't want to lock serde
    665    // at its old version, 0.1.1. Instead we want to allow it to update to
    666    // `0.1.3` and update its own dependencies (like above). To do this *all
    667    // crates from crates.io* are not locked (aka added to `avoid_locking`).
    668    // For dependencies like `log` their previous version in the lock file will
    669    // come up first before newer version, if newer version are available.
    670    {
    671        let _span = tracing::span!(tracing::Level::TRACE, "poison").entered();
    672        let mut path_deps = ws.members().cloned().collect::<Vec<_>>();
    673        let mut visited = HashSet::new();
    674        while let Some(member) = path_deps.pop() {
    675            if !visited.insert(member.package_id()) {
    676                continue;
    677            }
    678            let is_ws_member = ws.is_member(&member);
    679            for dep in member.dependencies() {
    680                // If this dependency didn't match anything special then we may want
    681                // to poison the source as it may have been added. If this path
    682                // dependencies is **not** a workspace member, however, and it's an
    683                // optional/non-transitive dependency then it won't be necessarily
    684                // be in our lock file. If this shows up then we avoid poisoning
    685                // this source as otherwise we'd repeatedly update the registry.
    686                //
    687                // TODO: this breaks adding an optional dependency in a
    688                // non-workspace member and then simultaneously editing the
    689                // dependency on that crate to enable the feature. For now,
    690                // this bug is better than the always-updating registry though.
    691                if !is_ws_member && (dep.is_optional() || !dep.is_transitive()) {
    692                    continue;
    693                }
    694
    695                // If dev-dependencies aren't being resolved, skip them.
    696                if !dep.is_transitive() && !dev_deps {
    697                    continue;
    698                }
    699
    700                // If this is a path dependency, then try to push it onto our
    701                // worklist.
    702                if let Some(pkg) = path_pkg(dep.source_id()) {
    703                    path_deps.push(pkg);
    704                    continue;
    705                }
    706
    707                // If we match *anything* in the dependency graph then we consider
    708                // ourselves all ok, and assume that we'll resolve to that.
    709                if resolve.iter().any(|id| dep.matches_ignoring_source(id)) {
    710                    continue;
    711                }
    712
    713                // Ok if nothing matches, then we poison the source of these
    714                // dependencies and the previous lock file.
    715                debug!(
    716                    "poisoning {} because {} looks like it changed {}",
    717                    dep.source_id(),
    718                    member.package_id(),
    719                    dep.package_name()
    720                );
    721                for id in resolve
    722                    .iter()
    723                    .filter(|id| id.source_id() == dep.source_id())
    724                {
    725                    add_deps(resolve, id, &mut avoid_locking);
    726                }
    727            }
    728        }
    729    }
    730
    731    // Additionally, here we process all path dependencies listed in the previous
    732    // resolve. They can not only have their dependencies change but also
    733    // the versions of the package change as well. If this ends up happening
    734    // then we want to make sure we don't lock a package ID node that doesn't
    735    // actually exist. Note that we don't do transitive visits of all the
    736    // package's dependencies here as that'll be covered below to poison those
    737    // if they changed.
    738    //
    739    // This must come after all other `add_deps` calls to ensure it recursively walks the tree when
    740    // called.
    741    for node in resolve.iter() {
    742        if let Some(pkg) = path_pkg(node.source_id()) {
    743            if pkg.package_id() != node {
    744                avoid_locking.insert(node);
    745            }
    746        }
    747    }
    748
    749    // Alright now that we've got our new, fresh, shiny, and refined `keep`
    750    // function let's put it to action. Take a look at the previous lock file,
    751    // filter everything by this callback, and then shove everything else into
    752    // the registry as a locked dependency.
    753    let keep = |id: &PackageId| keep(id) && !avoid_locking.contains(id);
    754
    755    registry.clear_lock();
    756    {
    757        let _span = tracing::span!(tracing::Level::TRACE, "register_lock").entered();
    758        for node in resolve.iter().filter(keep) {
    759            let deps = resolve
    760                .deps_not_replaced(node)
    761                .map(|p| p.0)
    762                .filter(keep)
    763                .collect::<Vec<_>>();
    764
    765            // In the v2 lockfile format and prior the `branch=master` dependency
    766            // directive was serialized the same way as the no-branch-listed
    767            // directive. Nowadays in Cargo, however, these two directives are
    768            // considered distinct and are no longer represented the same way. To
    769            // maintain compatibility with older lock files we register locked nodes
    770            // for *both* the master branch and the default branch.
    771            //
    772            // Note that this is only applicable for loading older resolves now at
    773            // this point. All new lock files are encoded as v3-or-later, so this is
    774            // just compat for loading an old lock file successfully.
    775            if let Some(node) = master_branch_git_source(node, resolve) {
    776                registry.register_lock(node, deps.clone());
    777            }
    778
    779            registry.register_lock(node, deps);
    780        }
    781    }
    782
    783    /// Recursively add `node` and all its transitive dependencies to `set`.
    784    fn add_deps(resolve: &Resolve, node: PackageId, set: &mut HashSet<PackageId>) {
    785        if !set.insert(node) {
    786            return;
    787        }
    788        debug!("ignoring any lock pointing directly at {}", node);
    789        for (dep, _) in resolve.deps_not_replaced(node) {
    790            add_deps(resolve, dep, set);
    791        }
    792    }
    793}
    794
    795fn master_branch_git_source(id: PackageId, resolve: &Resolve) -> Option<PackageId> {
    796    if resolve.version() <= ResolveVersion::V2 {
    797        let source = id.source_id();
    798        if let Some(GitReference::DefaultBranch) = source.git_reference() {
    799            let new_source =
    800                SourceId::for_git(source.url(), GitReference::Branch("master".to_string()))
    801                    .unwrap()
    802                    .with_precise_from(source);
    803            return Some(id.with_source_id(new_source));
    804        }
    805    }
    806    None
    807}
    808
    809/// Emits warnings of unused patches case by case.
    810///
    811/// This function does its best to provide more targeted and helpful
    812/// (such as showing close candidates that failed to match). However, that's
    813/// not terribly easy to do, so just show a general help message if we cannot.
    814fn emit_warnings_of_unused_patches(
    815    ws: &Workspace<'_>,
    816    resolve: &Resolve,
    817    registry: &PackageRegistry<'_>,
    818) -> CargoResult<()> {
    819    const MESSAGE: &str = "was not used in the crate graph";
    820
    821    // Patch package with the source URLs being patch
    822    let mut patch_pkgid_to_urls = HashMap::new();
    823    for (url, summaries) in registry.patches().iter() {
    824        for summary in summaries.iter() {
    825            patch_pkgid_to_urls
    826                .entry(summary.package_id())
    827                .or_insert_with(HashSet::new)
    828                .insert(url);
    829        }
    830    }
    831
    832    // pkg name -> all source IDs of under the same pkg name
    833    let mut source_ids_grouped_by_pkg_name = HashMap::new();
    834    for pkgid in resolve.iter() {
    835        source_ids_grouped_by_pkg_name
    836            .entry(pkgid.name())
    837            .or_insert_with(HashSet::new)
    838            .insert(pkgid.source_id());
    839    }
    840
    841    let mut unemitted_unused_patches = Vec::new();
    842    for unused in resolve.unused_patches().iter() {
    843        // Show alternative source URLs if the source URLs being patched
    844        // cannot be found in the crate graph.
    845        match (
    846            source_ids_grouped_by_pkg_name.get(&unused.name()),
    847            patch_pkgid_to_urls.get(unused),
    848        ) {
    849            (Some(ids), Some(patched_urls))
    850                if ids
    851                    .iter()
    852                    .all(|id| !patched_urls.contains(id.canonical_url())) =>
    853            {
    854                let mut help = "perhaps you meant one of the following:".to_owned();
    855                for id in ids {
    856                    help.push_str("\n\t");
    857                    help.push_str(&id.display_registry_name());
    858                }
    859                ws.gctx().shell().print_report(
    860                    &[Level::WARNING
    861                        .secondary_title(format!("patch `{unused}` {MESSAGE}"))
    862                        .element(Level::HELP.message(help))],
    863                    false,
    864                )?;
    865            }
    866            _ => unemitted_unused_patches.push(unused),
    867        }
    868    }
    869
    870    // Show general help message.
    871    if !unemitted_unused_patches.is_empty() {
    872        let mut warnings: Vec<_> = unemitted_unused_patches
    873            .iter()
    874            .map(|pkgid| {
    875                Group::with_title(
    876                    Level::WARNING.secondary_title(format!("patch `{pkgid}` {MESSAGE}")),
    877                )
    878            })
    879            .collect();
    880        warnings.push(Group::with_title(
    881            Level::HELP.secondary_title(UNUSED_PATCH_WARNING),
    882        ));
    883        ws.gctx().shell().print_report(&warnings, false)?;
    884    }
    885
    886    return Ok(());
    887}
    888
    889/// Informs `registry` and `version_pref` that `[patch]` entries are available
    890/// and preferable for the dependency resolution.
    891///
    892/// This returns a set of PackageIds of `[patch]` entries, and some related
    893/// locked PackageIds, for which locking should be avoided (but which will be
    894/// preferred when searching dependencies, via [`VersionPreferences::prefer_patch_deps`]).
    895#[tracing::instrument(level = "debug", skip_all, ret)]
    896fn register_patch_entries(
    897    registry: &mut PackageRegistry<'_>,
    898    ws: &Workspace<'_>,
    899    previous: Option<&Resolve>,
    900    version_prefs: &mut VersionPreferences,
    901    keep_previous: Keep<'_>,
    902) -> CargoResult<HashSet<PackageId>> {
    903    let mut avoid_patch_ids = HashSet::new();
    904    for (url, patches) in ws.root_patch()?.iter() {
    905        for patch in patches {
    906            version_prefs.prefer_dependency(patch.clone());
    907        }
    908        let Some(previous) = previous else {
    909            let patches: Vec<_> = patches.iter().map(|p| (p, None)).collect();
    910            let unlock_ids = registry.patch(url, &patches)?;
    911            // Since nothing is locked, this shouldn't possibly return anything.
    912            assert!(unlock_ids.is_empty());
    913            continue;
    914        };
    915
    916        // This is a list of pairs where the first element of the pair is
    917        // the raw `Dependency` which matches what's listed in `Cargo.toml`.
    918        // The second element is, if present, the "locked" version of
    919        // the `Dependency` as well as the `PackageId` that it previously
    920        // resolved to. This second element is calculated by looking at the
    921        // previous resolve graph, which is primarily what's done here to
    922        // build the `registrations` list.
    923        let mut registrations = Vec::new();
    924        for dep in patches {
    925            let candidates = || {
    926                previous
    927                    .iter()
    928                    .chain(previous.unused_patches().iter().cloned())
    929                    .filter(&keep_previous)
    930            };
    931
    932            let lock = match candidates().find(|id| dep.matches_id(*id)) {
    933                // If we found an exactly matching candidate in our list of
    934                // candidates, then that's the one to use.
    935                Some(package_id) => {
    936                    let mut locked_dep = dep.clone();
    937                    locked_dep.lock_to(package_id);
    938                    Some(LockedPatchDependency {
    939                        dependency: locked_dep,
    940                        package_id,
    941                        alt_package_id: None,
    942                    })
    943                }
    944                None => {
    945                    // If the candidate does not have a matching source id
    946                    // then we may still have a lock candidate. If we're
    947                    // loading a v2-encoded resolve graph and `dep` is a
    948                    // git dep with `branch = 'master'`, then this should
    949                    // also match candidates without `branch = 'master'`
    950                    // (which is now treated separately in Cargo).
    951                    //
    952                    // In this scenario we try to convert candidates located
    953                    // in the resolve graph to explicitly having the
    954                    // `master` branch (if they otherwise point to
    955                    // `DefaultBranch`). If this works and our `dep`
    956                    // matches that then this is something we'll lock to.
    957                    match candidates().find(|&id| match master_branch_git_source(id, previous) {
    958                        Some(id) => dep.matches_id(id),
    959                        None => false,
    960                    }) {
    961                        Some(id_using_default) => {
    962                            let id_using_master = id_using_default.with_source_id(
    963                                dep.source_id()
    964                                    .with_precise_from(id_using_default.source_id()),
    965                            );
    966
    967                            let mut locked_dep = dep.clone();
    968                            locked_dep.lock_to(id_using_master);
    969                            Some(LockedPatchDependency {
    970                                dependency: locked_dep,
    971                                package_id: id_using_master,
    972                                // Note that this is where the magic
    973                                // happens, where the resolve graph
    974                                // probably has locks pointing to
    975                                // DefaultBranch sources, and by including
    976                                // this here those will get transparently
    977                                // rewritten to Branch("master") which we
    978                                // have a lock entry for.
    979                                alt_package_id: Some(id_using_default),
    980                            })
    981                        }
    982
    983                        // No locked candidate was found
    984                        None => None,
    985                    }
    986                }
    987            };
    988
    989            registrations.push((dep, lock));
    990        }
    991
    992        let canonical = CanonicalUrl::new(url)?;
    993        for (orig_patch, unlock_id) in registry.patch(url, &registrations)? {
    994            // Avoid the locked patch ID.
    995            avoid_patch_ids.insert(unlock_id);
    996            // Also avoid the thing it is patching.
    997            avoid_patch_ids.extend(previous.iter().filter(|id| {
    998                orig_patch.matches_ignoring_source(*id)
    999                    && *id.source_id().canonical_url() == canonical
    1000            }));
    1001        }
    1002    }
    1003
    1004    Ok(avoid_patch_ids)
    1005}
    1006
    1007/// Locks each `[replace]` entry to a specific Package ID
    1008/// if the lockfile contains any corresponding previous replacement.
    1009fn lock_replacements(
    1010    ws: &Workspace<'_>,
    1011    previous: Option<&Resolve>,
    1012    keep: Keep<'_>,
    1013) -> Vec<(PackageIdSpec, Dependency)> {
    1014    let root_replace = ws.root_replace();
    1015    let replace = match previous {
    1016        Some(r) => root_replace
    1017            .iter()
    1018            .map(|(spec, dep)| {
    1019                for (&key, &val) in r.replacements().iter() {
    1020                    if spec.matches(key) && dep.matches_id(val) && keep(&val) {
    1021                        let mut dep = dep.clone();
    1022                        dep.lock_to(val);
    1023                        return (spec.clone(), dep);
    1024                    }
    1025                }
    1026                (spec.clone(), dep.clone())
    1027            })
    1028            .collect::<Vec<_>>(),
    1029        None => root_replace.to_vec(),
    1030    };
    1031    replace
    1032}