#
cargo/core/resolver/
features.rs

    1//! Resolves conditional compilation for [`features` section] in the manifest.
    2//!
    3//! This is a [new feature resolver] that runs independently of the main
    4//! dependency resolver. It has several options which can enable new feature
    5//! resolution behavior.
    6//!
    7//! One of its key characteristics is that it can avoid unifying features for
    8//! shared dependencies in some situations. See [`FeatureOpts`] for the
    9//! different behaviors that can be enabled. If no extra options are enabled,
    10//! then it should behave exactly the same as the dependency resolver's
    11//! feature resolution.
    12//!
    13//! The preferred way to engage this new resolver is via [`resolve_ws_with_opts`].
    14//!
    15//! This does not *replace* feature resolution in the dependency resolver, but
    16//! instead acts as a second pass which can *narrow* the features selected in
    17//! the dependency resolver. The dependency resolver still needs to do its own
    18//! feature resolution in order to avoid selecting optional dependencies that
    19//! are never enabled. The dependency resolver could, in theory, just assume
    20//! all optional dependencies on all packages are enabled (and remove all
    21//! knowledge of features), but that could introduce new requirements that
    22//! might change old behavior or cause conflicts. Maybe some day in the future
    23//! we could experiment with that, but it seems unlikely to work or be all
    24//! that helpful.
    25//!
    26//! ## Assumptions
    27//!
    28//! There are many assumptions made about the dependency resolver:
    29//!
    30//! * Assumes feature validation has already been done during the construction
    31//!   of feature maps, so the feature resolver doesn't do that validation at all.
    32//! * Assumes `dev-dependencies` within a dependency have been removed
    33//!   in the given [`Resolve`].
    34//!
    35//! There are probably other assumptions that I am forgetting.
    36//!
    37//! [`features` section]: https://doc.rust-lang.org/nightly/cargo/reference/features.html
    38//! [new feature resolver]: https://doc.rust-lang.org/nightly/cargo/reference/resolver.html#feature-resolver-version-2
    39//! [`resolve_ws_with_opts`]: crate::ops::resolve_ws_with_opts
    40
    41use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData};
    42use crate::core::dependency::{ArtifactTarget, DepKind, Dependency};
    43use crate::core::resolver::types::FeaturesSet;
    44use crate::core::resolver::{Resolve, ResolveBehavior};
    45use crate::core::{FeatureValue, PackageId, PackageIdSpec, PackageSet, Workspace};
    46use crate::util::interning::{InternedString, INTERNED_DEFAULT};
    47use crate::util::CargoResult;
    48use anyhow::{bail, Context};
    49use itertools::Itertools;
    50use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
    51use std::rc::Rc;
    52
    53/// The key used in various places to store features for a particular dependency.
    54/// The actual discrimination happens with the [`FeaturesFor`] type.
    55type PackageFeaturesKey = (PackageId, FeaturesFor);
    56/// Map of activated features.
    57type ActivateMap = HashMap<PackageFeaturesKey, BTreeSet<InternedString>>;
    58
    59/// Set of all activated features for all packages in the resolve graph.
    60pub struct ResolvedFeatures {
    61    activated_features: ActivateMap,
    62    /// Optional dependencies that should be built.
    63    ///
    64    /// The value is the `name_in_toml` of the dependencies.
    65    activated_dependencies: ActivateMap,
    66    opts: FeatureOpts,
    67}
    68
    69/// Options for how the feature resolver works.
    70#[derive(Default)]
    71pub struct FeatureOpts {
    72    /// Build deps and proc-macros will not share features with other dep kinds,
    73    /// and so won't artifact targets.
    74    /// In other terms, if true, features associated with certain kinds of dependencies
    75    /// will only be unified together.
    76    /// If false, there is only one namespace for features, unifying all features across
    77    /// all dependencies, no matter what kind.
    78    decouple_host_deps: bool,
    79    /// Dev dep features will not be activated unless needed.
    80    decouple_dev_deps: bool,
    81    /// Targets that are not in use will not activate features.
    82    ignore_inactive_targets: bool,
    83    /// If enabled, compare against old resolver (for testing).
    84    compare: bool,
    85}
    86
    87/// Flag to indicate if Cargo is building *any* dev units (tests, examples, etc.).
    88///
    89/// This disables decoupling of dev dependencies. It may be possible to relax
    90/// this in the future, but it will require significant changes to how unit
    91/// dependencies are computed, and can result in longer build times with
    92/// `cargo test` because the lib may need to be built 3 times instead of
    93/// twice.
    94#[derive(Copy, Clone, PartialEq)]
    95pub enum HasDevUnits {
    96    Yes,
    97    No,
    98}
    99
    100/// Flag to indicate that target-specific filtering should be disabled.
    101#[derive(Copy, Clone, PartialEq)]
    102pub enum ForceAllTargets {
    103    Yes,
    104    No,
    105}
    106
    107/// Flag to indicate if features are requested for a certain type of dependency.
    108///
    109/// This is primarily used for constructing a [`PackageFeaturesKey`] to decouple
    110/// activated features of the same package with different types of dependency.
    111#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
    112pub enum FeaturesFor {
    113    /// Normal or dev dependency.
    114    #[default]
    115    NormalOrDev,
    116    /// Build dependency or proc-macro.
    117    HostDep,
    118    /// Any dependency with both artifact and target specified.
    119    ///
    120    /// That is, `dep = { …, artifact = <crate-type>, target = <triple> }`
    121    ArtifactDep(CompileTarget),
    122}
    123
    124impl std::fmt::Display for FeaturesFor {
    125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    126        match self {
    127            FeaturesFor::HostDep => f.write_str("host"),
    128            FeaturesFor::ArtifactDep(target) => f.write_str(&target.rustc_target()),
    129            FeaturesFor::NormalOrDev => Ok(()),
    130        }
    131    }
    132}
    133
    134impl FeaturesFor {
    135    pub fn from_for_host(for_host: bool) -> FeaturesFor {
    136        if for_host {
    137            FeaturesFor::HostDep
    138        } else {
    139            FeaturesFor::NormalOrDev
    140        }
    141    }
    142
    143    pub fn from_for_host_or_artifact_target(
    144        for_host: bool,
    145        artifact_target: Option<CompileTarget>,
    146    ) -> FeaturesFor {
    147        match artifact_target {
    148            Some(target) => FeaturesFor::ArtifactDep(target),
    149            None => {
    150                if for_host {
    151                    FeaturesFor::HostDep
    152                } else {
    153                    FeaturesFor::NormalOrDev
    154                }
    155            }
    156        }
    157    }
    158
    159    fn apply_opts(self, opts: &FeatureOpts) -> Self {
    160        if opts.decouple_host_deps {
    161            self
    162        } else {
    163            FeaturesFor::default()
    164        }
    165    }
    166}
    167
    168impl FeatureOpts {
    169    pub fn new(
    170        ws: &Workspace<'_>,
    171        has_dev_units: HasDevUnits,
    172        force_all_targets: ForceAllTargets,
    173    ) -> CargoResult<FeatureOpts> {
    174        let mut opts = FeatureOpts::default();
    175        let unstable_flags = ws.gctx().cli_unstable();
    176        let mut enable = |feat_opts: &Vec<String>| {
    177            for opt in feat_opts {
    178                match opt.as_ref() {
    179                    "build_dep" | "host_dep" => opts.decouple_host_deps = true,
    180                    "dev_dep" => opts.decouple_dev_deps = true,
    181                    "itarget" => opts.ignore_inactive_targets = true,
    182                    "all" => {
    183                        opts.decouple_host_deps = true;
    184                        opts.decouple_dev_deps = true;
    185                        opts.ignore_inactive_targets = true;
    186                    }
    187                    "compare" => opts.compare = true,
    188                    "ws" => unimplemented!(),
    189                    s => bail!("-Zfeatures flag `{}` is not supported", s),
    190                }
    191            }
    192            Ok(())
    193        };
    194        if let Some(feat_opts) = unstable_flags.features.as_ref() {
    195            enable(feat_opts)?;
    196        }
    197        match ws.resolve_behavior() {
    198            ResolveBehavior::V1 => {}
    199            ResolveBehavior::V2 | ResolveBehavior::V3 => {
    200                enable(&vec!["all".to_string()]).unwrap();
    201            }
    202        }
    203        if let HasDevUnits::Yes = has_dev_units {
    204            // Dev deps cannot be decoupled when they are in use.
    205            opts.decouple_dev_deps = false;
    206        }
    207        if let ForceAllTargets::Yes = force_all_targets {
    208            opts.ignore_inactive_targets = false;
    209        }
    210        Ok(opts)
    211    }
    212
    213    /// Creates a new `FeatureOpts` for the given behavior.
    214    pub fn new_behavior(behavior: ResolveBehavior, has_dev_units: HasDevUnits) -> FeatureOpts {
    215        match behavior {
    216            ResolveBehavior::V1 => FeatureOpts::default(),
    217            ResolveBehavior::V2 | ResolveBehavior::V3 => FeatureOpts {
    218                decouple_host_deps: true,
    219                decouple_dev_deps: has_dev_units == HasDevUnits::No,
    220                ignore_inactive_targets: true,
    221                compare: false,
    222            },
    223        }
    224    }
    225}
    226
    227/// Features flags requested for a package.
    228///
    229/// This should be cheap and fast to clone, it is used in the resolver for
    230/// various caches.
    231///
    232/// This is split into enum variants because the resolver needs to handle
    233/// features coming from different places (command-line and dependency
    234/// declarations), but those different places have different constraints on
    235/// which syntax is allowed. This helps ensure that every place dealing with
    236/// features is properly handling those syntax restrictions.
    237#[derive(Debug, Clone, Eq, PartialEq, Hash)]
    238pub enum RequestedFeatures {
    239    /// Features requested on the command-line with flags.
    240    CliFeatures(CliFeatures),
    241    /// Features specified in a dependency declaration.
    242    DepFeatures {
    243        /// The `features` dependency field.
    244        features: FeaturesSet,
    245        /// The `default-features` dependency field.
    246        uses_default_features: bool,
    247    },
    248}
    249
    250/// Features specified on the command-line.
    251#[derive(Debug, Clone, Eq, PartialEq, Hash)]
    252pub struct CliFeatures {
    253    /// Features from the `--features` flag.
    254    pub features: Rc<BTreeSet<FeatureValue>>,
    255    /// The `--all-features` flag.
    256    pub all_features: bool,
    257    /// Inverse of `--no-default-features` flag.
    258    pub uses_default_features: bool,
    259}
    260
    261impl CliFeatures {
    262    /// Creates a new `CliFeatures` from the given command-line flags.
    263    pub fn from_command_line(
    264        features: &[String],
    265        all_features: bool,
    266        uses_default_features: bool,
    267    ) -> CargoResult<CliFeatures> {
    268        let features = Rc::new(CliFeatures::split_features(features));
    269        // Some early validation to ensure correct syntax.
    270        for feature in features.iter() {
    271            match feature {
    272                // Maybe call validate_feature_name here once it is an error?
    273                FeatureValue::Feature(_) => {}
    274                FeatureValue::Dep { .. } => {
    275                    bail!(
    276                        "feature `{}` is not allowed to use explicit `dep:` syntax",
    277                        feature
    278                    );
    279                }
    280                FeatureValue::DepFeature { dep_feature, .. } => {
    281                    if dep_feature.contains('/') {
    282                        bail!("multiple slashes in feature `{}` is not allowed", feature);
    283                    }
    284                }
    285            }
    286        }
    287        Ok(CliFeatures {
    288            features,
    289            all_features,
    290            uses_default_features,
    291        })
    292    }
    293
    294    /// Creates a new `CliFeatures` with the given `all_features` setting.
    295    pub fn new_all(all_features: bool) -> CliFeatures {
    296        CliFeatures {
    297            features: Rc::new(BTreeSet::new()),
    298            all_features,
    299            uses_default_features: true,
    300        }
    301    }
    302
    303    fn split_features(features: &[String]) -> BTreeSet<FeatureValue> {
    304        features
    305            .iter()
    306            .flat_map(|s| s.split_whitespace())
    307            .flat_map(|s| s.split(','))
    308            .filter(|s| !s.is_empty())
    309            .map(|s| s.into())
    310            .map(FeatureValue::new)
    311            .collect()
    312    }
    313}
    314
    315impl ResolvedFeatures {
    316    /// Returns the list of features that are enabled for the given package.
    317    pub fn activated_features(
    318        &self,
    319        pkg_id: PackageId,
    320        features_for: FeaturesFor,
    321    ) -> Vec<InternedString> {
    322        if let Some(res) = self.activated_features_unverified(pkg_id, features_for) {
    323            res
    324        } else {
    325            panic!(
    326                "did not find features for ({pkg_id:?}, {features_for:?}) within activated_features:\n{:#?}",
    327                self.activated_features.keys()
    328            )
    329        }
    330    }
    331
    332    /// Variant of `activated_features` that returns `None` if this is
    333    /// not a valid `pkg_id/is_build` combination. Used in places which do
    334    /// not know which packages are activated (like `cargo clean`).
    335    pub fn activated_features_unverified(
    336        &self,
    337        pkg_id: PackageId,
    338        features_for: FeaturesFor,
    339    ) -> Option<Vec<InternedString>> {
    340        let fk = features_for.apply_opts(&self.opts);
    341        if let Some(fs) = self.activated_features.get(&(pkg_id, fk)) {
    342            Some(fs.iter().cloned().collect())
    343        } else {
    344            None
    345        }
    346    }
    347
    348    /// Returns if the given dependency should be included.
    349    ///
    350    /// This handles dependencies disabled via `cfg` expressions and optional
    351    /// dependencies which are not enabled.
    352    pub fn is_dep_activated(
    353        &self,
    354        pkg_id: PackageId,
    355        features_for: FeaturesFor,
    356        dep_name: InternedString,
    357    ) -> bool {
    358        let key = features_for.apply_opts(&self.opts);
    359        self.activated_dependencies
    360            .get(&(pkg_id, key))
    361            .map(|deps| deps.contains(&dep_name))
    362            .unwrap_or(false)
    363    }
    364
    365    /// Compares the result against the original resolver behavior.
    366    ///
    367    /// Used by `cargo fix --edition` to display any differences.
    368    pub fn compare_legacy(&self, legacy: &ResolvedFeatures) -> DiffMap {
    369        self.activated_features
    370            .iter()
    371            .filter_map(|((pkg_id, for_host), new_features)| {
    372                let old_features = legacy
    373                    .activated_features
    374                    .get(&(*pkg_id, *for_host))
    375                    // The new features may have for_host entries where the old one does not.
    376                    .or_else(|| {
    377                        legacy
    378                            .activated_features
    379                            .get(&(*pkg_id, FeaturesFor::default()))
    380                    })
    381                    .map(|feats| feats.iter().cloned().collect())
    382                    .unwrap_or_else(|| BTreeSet::new());
    383                // The new resolver should never add features.
    384                assert_eq!(new_features.difference(&old_features).next(), None);
    385                let removed_features: BTreeSet<_> =
    386                    old_features.difference(new_features).cloned().collect();
    387                if removed_features.is_empty() {
    388                    None
    389                } else {
    390                    Some(((*pkg_id, *for_host), removed_features))
    391                }
    392            })
    393            .collect()
    394    }
    395}
    396
    397/// Map of differences.
    398///
    399/// Key is `(pkg_id, for_host)`. Value is a set of features or dependencies removed.
    400pub type DiffMap = BTreeMap<PackageFeaturesKey, BTreeSet<InternedString>>;
    401
    402/// The new feature resolver that [`resolve`]s your project.
    403///
    404/// For more information, please see the [module-level documentation].
    405///
    406/// [`resolve`]: Self::resolve
    407/// [module-level documentation]: crate::core::resolver::features
    408pub struct FeatureResolver<'a, 'gctx> {
    409    ws: &'a Workspace<'gctx>,
    410    target_data: &'a mut RustcTargetData<'gctx>,
    411    /// The platforms to build for, requested by the user.
    412    requested_targets: &'a [CompileKind],
    413    resolve: &'a Resolve,
    414    package_set: &'a PackageSet<'gctx>,
    415    /// Options that change how the feature resolver operates.
    416    opts: FeatureOpts,
    417    /// Map of features activated for each package.
    418    activated_features: ActivateMap,
    419    /// Map of optional dependencies activated for each package.
    420    activated_dependencies: ActivateMap,
    421    /// Keeps track of which packages have had its dependencies processed.
    422    /// Used to avoid cycles, and to speed up processing.
    423    processed_deps: HashSet<PackageFeaturesKey>,
    424    /// If this is `true`, then a non-default `feature_key` needs to be tracked while
    425    /// traversing the graph.
    426    ///
    427    /// This is only here to avoid calling [`has_any_proc_macro`] when all feature
    428    /// options are disabled (because [`has_any_proc_macro`] can trigger downloads).
    429    /// This has to be separate from [`FeatureOpts::decouple_host_deps`] because
    430    /// `for_host` tracking is also needed for `itarget` to work properly.
    431    ///
    432    /// [`has_any_proc_macro`]: FeatureResolver::has_any_proc_macro
    433    track_for_host: bool,
    434    /// `dep_name?/feat_name` features that will be activated if `dep_name` is
    435    /// ever activated.
    436    ///
    437    /// The key is the `(package, for_host, dep_name)` of the package whose
    438    /// dependency will trigger the addition of new features. The value is the
    439    /// set of features to activate.
    440    deferred_weak_dependencies:
    441        HashMap<(PackageId, FeaturesFor, InternedString), HashSet<InternedString>>,
    442}
    443
    444impl<'a, 'gctx> FeatureResolver<'a, 'gctx> {
    445    /// Runs the resolution algorithm and returns a new [`ResolvedFeatures`]
    446    /// with the result.
    447    #[tracing::instrument(skip_all)]
    448    pub fn resolve(
    449        ws: &Workspace<'gctx>,
    450        target_data: &'a mut RustcTargetData<'gctx>,
    451        resolve: &Resolve,
    452        package_set: &'a PackageSet<'gctx>,
    453        cli_features: &CliFeatures,
    454        specs: &[PackageIdSpec],
    455        requested_targets: &[CompileKind],
    456        opts: FeatureOpts,
    457    ) -> CargoResult<ResolvedFeatures> {
    458        let track_for_host = opts.decouple_host_deps || opts.ignore_inactive_targets;
    459        let mut r = FeatureResolver {
    460            ws,
    461            target_data,
    462            requested_targets,
    463            resolve,
    464            package_set,
    465            opts,
    466            activated_features: HashMap::new(),
    467            activated_dependencies: HashMap::new(),
    468            processed_deps: HashSet::new(),
    469            track_for_host,
    470            deferred_weak_dependencies: HashMap::new(),
    471        };
    472        r.do_resolve(specs, cli_features)?;
    473        tracing::debug!("features={:#?}", r.activated_features);
    474        if r.opts.compare {
    475            r.compare();
    476        }
    477        Ok(ResolvedFeatures {
    478            activated_features: r.activated_features,
    479            activated_dependencies: r.activated_dependencies,
    480            opts: r.opts,
    481        })
    482    }
    483
    484    /// Performs the process of resolving all features for the resolve graph.
    485    fn do_resolve(
    486        &mut self,
    487        specs: &[PackageIdSpec],
    488        cli_features: &CliFeatures,
    489    ) -> CargoResult<()> {
    490        let member_features = self.ws.members_with_features(specs, cli_features)?;
    491        for (member, cli_features) in &member_features {
    492            let fvs = self.fvs_from_requested(member.package_id(), cli_features);
    493            let fk = if self.track_for_host && self.has_any_proc_macro(member.package_id()) {
    494                // Also activate for normal dependencies. This is needed if the
    495                // proc-macro includes other targets (like binaries or tests),
    496                // or running in `cargo test`. Note that in a workspace, if
    497                // the proc-macro is selected on the command like (like with
    498                // `--workspace`), this forces feature unification with normal
    499                // dependencies. This is part of the bigger problem where
    500                // features depend on which packages are built.
    501                self.activate_pkg(member.package_id(), FeaturesFor::default(), &fvs)?;
    502                FeaturesFor::HostDep
    503            } else {
    504                FeaturesFor::default()
    505            };
    506            self.activate_pkg(member.package_id(), fk, &fvs)?;
    507        }
    508        Ok(())
    509    }
    510
    511    /// Activates [`FeatureValue`]s on the given package.
    512    ///
    513    /// This is the main entrance into the recursion of feature activation
    514    /// for a package.
    515    fn activate_pkg(
    516        &mut self,
    517        pkg_id: PackageId,
    518        fk: FeaturesFor,
    519        fvs: &[FeatureValue],
    520    ) -> CargoResult<()> {
    521        tracing::trace!("activate_pkg {} {}", pkg_id.name(), fk);
    522        // Add an empty entry to ensure everything is covered. This is intended for
    523        // finding bugs where the resolver missed something it should have visited.
    524        // Remove this in the future if `activated_features` uses an empty default.
    525        self.activated_features
    526            .entry((pkg_id, fk.apply_opts(&self.opts)))
    527            .or_insert_with(BTreeSet::new);
    528        for fv in fvs {
    529            self.activate_fv(pkg_id, fk, fv)?;
    530        }
    531        if !self.processed_deps.insert((pkg_id, fk)) {
    532            // Already processed dependencies. There's no need to process them
    533            // again. This is primarily to avoid cycles, but also helps speed
    534            // things up.
    535            //
    536            // This is safe because if another package comes along and adds a
    537            // feature on this package, it will immediately add it (in
    538            // `activate_fv`), and recurse as necessary right then and there.
    539            // For example, consider we've already processed our dependencies,
    540            // and another package comes along and enables one of our optional
    541            // dependencies, it will do so immediately in the
    542            // `FeatureValue::DepFeature` branch, and then immediately
    543            // recurse into that optional dependency. This also holds true for
    544            // features that enable other features.
    545            return Ok(());
    546        }
    547        for (dep_pkg_id, deps) in self.deps(pkg_id, fk)? {
    548            for (dep, dep_fk) in deps {
    549                if dep.is_optional() {
    550                    // Optional dependencies are enabled in `activate_fv` when
    551                    // a feature enables it.
    552                    continue;
    553                }
    554                // Recurse into the dependency.
    555                let fvs = self.fvs_from_dependency(dep_pkg_id, dep);
    556                self.activate_pkg(dep_pkg_id, dep_fk, &fvs)?;
    557            }
    558        }
    559        Ok(())
    560    }
    561
    562    /// Activate a single `FeatureValue` for a package.
    563    fn activate_fv(
    564        &mut self,
    565        pkg_id: PackageId,
    566        fk: FeaturesFor,
    567        fv: &FeatureValue,
    568    ) -> CargoResult<()> {
    569        tracing::trace!("activate_fv {} {} {}", pkg_id.name(), fk, fv);
    570        match fv {
    571            FeatureValue::Feature(f) => {
    572                self.activate_rec(pkg_id, fk, *f)?;
    573            }
    574            FeatureValue::Dep { dep_name } => {
    575                self.activate_dependency(pkg_id, fk, *dep_name)?;
    576            }
    577            FeatureValue::DepFeature {
    578                dep_name,
    579                dep_feature,
    580                weak,
    581            } => {
    582                self.activate_dep_feature(pkg_id, fk, *dep_name, *dep_feature, *weak)?;
    583            }
    584        }
    585        Ok(())
    586    }
    587
    588    /// Activate the given feature for the given package, and then recursively
    589    /// activate any other features that feature enables.
    590    fn activate_rec(
    591        &mut self,
    592        pkg_id: PackageId,
    593        fk: FeaturesFor,
    594        feature_to_enable: InternedString,
    595    ) -> CargoResult<()> {
    596        tracing::trace!(
    597            "activate_rec {} {} feat={}",
    598            pkg_id.name(),
    599            fk,
    600            feature_to_enable
    601        );
    602        let enabled = self
    603            .activated_features
    604            .entry((pkg_id, fk.apply_opts(&self.opts)))
    605            .or_insert_with(BTreeSet::new);
    606        if !enabled.insert(feature_to_enable) {
    607            // Already enabled.
    608            return Ok(());
    609        }
    610        let summary = self.resolve.summary(pkg_id);
    611        let feature_map = summary.features();
    612        let Some(fvs) = feature_map.get(&feature_to_enable) else {
    613            // TODO: this should only happen for optional dependencies.
    614            // Other cases should be validated by Summary's `build_feature_map`.
    615            // Figure out some way to validate this assumption.
    616            tracing::debug!(
    617                "pkg {:?} does not define feature {}",
    618                pkg_id,
    619                feature_to_enable
    620            );
    621            return Ok(());
    622        };
    623        for fv in fvs {
    624            self.activate_fv(pkg_id, fk, fv)?;
    625        }
    626        Ok(())
    627    }
    628
    629    /// Activate a dependency (`dep:dep_name` syntax).
    630    fn activate_dependency(
    631        &mut self,
    632        pkg_id: PackageId,
    633        fk: FeaturesFor,
    634        dep_name: InternedString,
    635    ) -> CargoResult<()> {
    636        // Mark this dependency as activated.
    637        let save_decoupled = fk.apply_opts(&self.opts);
    638        self.activated_dependencies
    639            .entry((pkg_id, save_decoupled))
    640            .or_default()
    641            .insert(dep_name);
    642        // Check for any deferred features.
    643        let to_enable = self
    644            .deferred_weak_dependencies
    645            .remove(&(pkg_id, fk, dep_name));
    646        // Activate the optional dep.
    647        for (dep_pkg_id, deps) in self.deps(pkg_id, fk)? {
    648            for (dep, dep_fk) in deps {
    649                if dep.name_in_toml() != dep_name {
    650                    continue;
    651                }
    652                if let Some(to_enable) = &to_enable {
    653                    for dep_feature in to_enable {
    654                        tracing::trace!(
    655                            "activate deferred {} {} -> {}/{}",
    656                            pkg_id.name(),
    657                            fk,
    658                            dep_name,
    659                            dep_feature
    660                        );
    661                        let fv = FeatureValue::new(*dep_feature);
    662                        self.activate_fv(dep_pkg_id, dep_fk, &fv)?;
    663                    }
    664                }
    665                let fvs = self.fvs_from_dependency(dep_pkg_id, dep);
    666                self.activate_pkg(dep_pkg_id, dep_fk, &fvs)?;
    667            }
    668        }
    669        Ok(())
    670    }
    671
    672    /// Activate a feature within a dependency (`dep_name/feat_name` syntax).
    673    fn activate_dep_feature(
    674        &mut self,
    675        pkg_id: PackageId,
    676        fk: FeaturesFor,
    677        dep_name: InternedString,
    678        dep_feature: InternedString,
    679        weak: bool,
    680    ) -> CargoResult<()> {
    681        for (dep_pkg_id, deps) in self.deps(pkg_id, fk)? {
    682            for (dep, dep_fk) in deps {
    683                if dep.name_in_toml() != dep_name {
    684                    continue;
    685                }
    686                if dep.is_optional() {
    687                    let save_for_host = fk.apply_opts(&self.opts);
    688                    if weak
    689                        && !self
    690                            .activated_dependencies
    691                            .get(&(pkg_id, save_for_host))
    692                            .map(|deps| deps.contains(&dep_name))
    693                            .unwrap_or(false)
    694                    {
    695                        // This is weak, but not yet activated. Defer in case
    696                        // something comes along later and enables it.
    697                        tracing::trace!(
    698                            "deferring feature {} {} -> {}/{}",
    699                            pkg_id.name(),
    700                            fk,
    701                            dep_name,
    702                            dep_feature
    703                        );
    704                        self.deferred_weak_dependencies
    705                            .entry((pkg_id, fk, dep_name))
    706                            .or_default()
    707                            .insert(dep_feature);
    708                        continue;
    709                    }
    710
    711                    // Activate the dependency on self.
    712                    let fv = FeatureValue::Dep { dep_name };
    713                    self.activate_fv(pkg_id, fk, &fv)?;
    714                    if !weak {
    715                        // The old behavior before weak dependencies were
    716                        // added is to also enables a feature of the same
    717                        // name.
    718                        //
    719                        // Don't enable if the implicit optional dependency
    720                        // feature wasn't created due to `dep:` hiding.
    721                        // See rust-lang/cargo#10788 and rust-lang/cargo#12130
    722                        let summary = self.resolve.summary(pkg_id);
    723                        let feature_map = summary.features();
    724                        if feature_map.contains_key(&dep_name) {
    725                            self.activate_rec(pkg_id, fk, dep_name)?;
    726                        }
    727                    }
    728                }
    729                // Activate the feature on the dependency.
    730                let fv = FeatureValue::new(dep_feature);
    731                self.activate_fv(dep_pkg_id, dep_fk, &fv)?;
    732            }
    733        }
    734        Ok(())
    735    }
    736
    737    /// Returns Vec of `FeatureValues` from a Dependency definition.
    738    fn fvs_from_dependency(&self, dep_id: PackageId, dep: &Dependency) -> Vec<FeatureValue> {
    739        let summary = self.resolve.summary(dep_id);
    740        let feature_map = summary.features();
    741        let mut result: Vec<FeatureValue> = dep
    742            .features()
    743            .iter()
    744            .map(|f| FeatureValue::new(*f))
    745            .collect();
    746        if dep.uses_default_features() && feature_map.contains_key(&INTERNED_DEFAULT) {
    747            result.push(FeatureValue::Feature(INTERNED_DEFAULT));
    748        }
    749        result
    750    }
    751
    752    /// Returns Vec of `FeatureValues` from a set of command-line features.
    753    fn fvs_from_requested(
    754        &self,
    755        pkg_id: PackageId,
    756        cli_features: &CliFeatures,
    757    ) -> Vec<FeatureValue> {
    758        let summary = self.resolve.summary(pkg_id);
    759        let feature_map = summary.features();
    760
    761        let mut result: Vec<FeatureValue> = cli_features.features.iter().cloned().collect();
    762        if cli_features.uses_default_features && feature_map.contains_key(&INTERNED_DEFAULT) {
    763            result.push(FeatureValue::Feature(INTERNED_DEFAULT));
    764        }
    765
    766        if cli_features.all_features {
    767            result.extend(feature_map.keys().map(|k| FeatureValue::Feature(*k)))
    768        }
    769
    770        result
    771    }
    772
    773    /// Returns the dependencies for a package, filtering out inactive targets.
    774    fn deps(
    775        &mut self,
    776        pkg_id: PackageId,
    777        fk: FeaturesFor,
    778    ) -> CargoResult<Vec<(PackageId, Vec<(&'a Dependency, FeaturesFor)>)>> {
    779        // Helper for determining if a platform is activated.
    780        fn platform_activated(
    781            dep: &Dependency,
    782            fk: FeaturesFor,
    783            target_data: &RustcTargetData<'_>,
    784            requested_targets: &[CompileKind],
    785        ) -> bool {
    786            // We always count platforms as activated if the target stems from an artifact
    787            // dependency's target specification. This triggers in conjunction with
    788            // `[target.'cfg(…)'.dependencies]` manifest sections.
    789            match (dep.is_build(), fk) {
    790                (true, _) | (_, FeaturesFor::HostDep) => {
    791                    // We always care about build-dependencies, and they are always
    792                    // Host. If we are computing dependencies "for a build script",
    793                    // even normal dependencies are host-only.
    794                    target_data.dep_platform_activated(dep, CompileKind::Host)
    795                }
    796                (_, FeaturesFor::NormalOrDev) => requested_targets
    797                    .iter()
    798                    .any(|kind| target_data.dep_platform_activated(dep, *kind)),
    799                (_, FeaturesFor::ArtifactDep(target)) => {
    800                    target_data.dep_platform_activated(dep, CompileKind::Target(target))
    801                }
    802            }
    803        }
    804
    805        /// Returns the `FeaturesFor` needed for this dependency.
    806        ///
    807        /// This includes the `FeaturesFor` for artifact dependencies, which
    808        /// might specify multiple targets.
    809        fn artifact_features_for(
    810            this: &mut FeatureResolver<'_, '_>,
    811            pkg_id: PackageId,
    812            dep: &Dependency,
    813            lib_fk: FeaturesFor,
    814        ) -> CargoResult<Vec<FeaturesFor>> {
    815            let Some(artifact) = dep.artifact() else {
    816                return Ok(vec![lib_fk]);
    817            };
    818            let mut result = Vec::new();
    819            let host_triple = this.target_data.rustc.host;
    820            // Not all targets may be queried before resolution since artifact
    821            // dependencies and per-pkg-targets are not immediately known.
    822            let mut activate_target = |target| {
    823                let name = dep.name_in_toml();
    824                this.target_data
    825                    .merge_compile_kind(CompileKind::Target(target))
    826                    .with_context(|| {
    827                        format!(
    828                            "failed to determine target information for target `{target}`.\n  \
    829                             Artifact dependency `{name}` in package `{pkg_id}` requires building \
    830                             for `{target}`",
    831                            target = target.rustc_target()
    832                        )
    833                    })
    834            };
    835
    836            if let Some(target) = artifact.target() {
    837                match target {
    838                    ArtifactTarget::Force(target) => {
    839                        activate_target(target)?;
    840                        result.push(FeaturesFor::ArtifactDep(target))
    841                    }
    842                    // FIXME: this needs to interact with the `default-target`
    843                    // and `forced-target` values of the dependency
    844                    ArtifactTarget::BuildDependencyAssumeTarget => {
    845                        for kind in this.requested_targets {
    846                            let target = match kind {
    847                                CompileKind::Host => CompileTarget::new(&host_triple).unwrap(),
    848                                CompileKind::Target(target) => *target,
    849                            };
    850                            activate_target(target)?;
    851                            result.push(FeaturesFor::ArtifactDep(target));
    852                        }
    853                    }
    854                }
    855            }
    856            if artifact.is_lib() || artifact.target().is_none() {
    857                result.push(lib_fk);
    858            }
    859            Ok(result)
    860        }
    861
    862        self.resolve
    863            .deps(pkg_id)
    864            .map(|(dep_id, deps)| {
    865                let deps = deps
    866                    .iter()
    867                    .filter(|dep| {
    868                        if dep.platform().is_some()
    869                            && self.opts.ignore_inactive_targets
    870                            && !platform_activated(
    871                                dep,
    872                                fk,
    873                                self.target_data,
    874                                self.requested_targets,
    875                            )
    876                        {
    877                            return false;
    878                        }
    879                        if self.opts.decouple_dev_deps && dep.kind() == DepKind::Development {
    880                            return false;
    881                        }
    882                        true
    883                    })
    884                    .collect_vec() // collect because the next closure mutably borrows `self.target_data`
    885                    .into_iter()
    886                    .map(|dep| {
    887                        // Each `dep`endency can be built for multiple targets. For one, it
    888                        // may be a library target which is built as initially configured
    889                        // by `fk`. If it appears as build dependency, it must be built
    890                        // for the host.
    891                        //
    892                        // It may also be an artifact dependency,
    893                        // which could be built either
    894                        //
    895                        //  - for a specified (aka 'forced') target, specified by
    896                        //    `dep = { …, target = <triple>` }`
    897                        //  - as an artifact for use in build dependencies that should
    898                        //    build for whichever `--target`s are specified
    899                        //  - like a library would be built
    900                        //
    901                        // Generally, the logic for choosing a target for dependencies is
    902                        // unaltered and used to determine how to build non-artifacts,
    903                        // artifacts without target specification and no library,
    904                        // or an artifacts library.
    905                        //
    906                        // All this may result in a dependency being built multiple times
    907                        // for various targets which are either specified in the manifest
    908                        // or on the cargo command-line.
    909                        let lib_fk = if fk == FeaturesFor::default() {
    910                            (self.track_for_host
    911                                && (dep.is_build() || self.has_proc_macro_lib(dep_id)))
    912                            .then(|| FeaturesFor::HostDep)
    913                            .unwrap_or_default()
    914                        } else {
    915                            fk
    916                        };
    917
    918                        let dep_fks = artifact_features_for(self, pkg_id, dep, lib_fk)?;
    919                        Ok(dep_fks.into_iter().map(move |dep_fk| (dep, dep_fk)))
    920                    })
    921                    .flatten_ok()
    922                    .collect::<CargoResult<Vec<_>>>()?;
    923                Ok((dep_id, deps))
    924            })
    925            .filter(|res| res.as_ref().map_or(true, |(_id, deps)| !deps.is_empty()))
    926            .collect()
    927    }
    928
    929    /// Compare the activated features to the resolver. Used for testing.
    930    fn compare(&self) {
    931        let mut found = false;
    932        for ((pkg_id, dep_kind), features) in &self.activated_features {
    933            let r_features = self.resolve.features(*pkg_id);
    934            if !r_features.iter().eq(features.iter()) {
    935                crate::drop_eprintln!(
    936                    self.ws.gctx(),
    937                    "{}/{:?} features mismatch\nresolve: {:?}\nnew: {:?}\n",
    938                    pkg_id,
    939                    dep_kind,
    940                    r_features,
    941                    features
    942                );
    943                found = true;
    944            }
    945        }
    946        if found {
    947            panic!("feature mismatch");
    948        }
    949    }
    950
    951    /// Whether the given package has any proc macro target, including proc-macro examples.
    952    fn has_any_proc_macro(&self, package_id: PackageId) -> bool {
    953        self.package_set
    954            .get_one(package_id)
    955            .expect("packages downloaded")
    956            .proc_macro()
    957    }
    958
    959    /// Whether the given package is a proc macro lib target.
    960    ///
    961    /// This is useful for checking if a dependency is a proc macro,
    962    /// as it is not possible to depend on a non-lib target as a proc-macro.
    963    fn has_proc_macro_lib(&self, package_id: PackageId) -> bool {
    964        self.package_set
    965            .get_one(package_id)
    966            .expect("packages downloaded")
    967            .library()
    968            .map(|lib| lib.proc_macro())
    969            .unwrap_or_default()
    970    }
    971}