Skip to main content

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