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(InternedString::new)
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        self.resolve
806            .deps(pkg_id)
807            .map(|(dep_id, deps)| {
808                let deps = deps
809                    .iter()
810                    .filter(|dep| {
811                        if dep.platform().is_some()
812                            && self.opts.ignore_inactive_targets
813                            && !platform_activated(
814                                dep,
815                                fk,
816                                self.target_data,
817                                self.requested_targets,
818                            )
819                        {
820                            return false;
821                        }
822                        if self.opts.decouple_dev_deps && dep.kind() == DepKind::Development {
823                            return false;
824                        }
825                        true
826                    })
827                    .collect_vec() // collect because the next closure mutably borrows `self.target_data`
828                    .into_iter()
829                    .map(|dep| {
830                        // Each `dep`endency can be built for multiple targets. For one, it
831                        // may be a library target which is built as initially configured
832                        // by `fk`. If it appears as build dependency, it must be built
833                        // for the host.
834                        //
835                        // It may also be an artifact dependency,
836                        // which could be built either
837                        //
838                        //  - for a specified (aka 'forced') target, specified by
839                        //    `dep = { …, target = <triple>` }`
840                        //  - as an artifact for use in build dependencies that should
841                        //    build for whichever `--target`s are specified
842                        //  - like a library would be built
843                        //
844                        // Generally, the logic for choosing a target for dependencies is
845                        // unaltered and used to determine how to build non-artifacts,
846                        // artifacts without target specification and no library,
847                        // or an artifacts library.
848                        //
849                        // All this may result in a dependency being built multiple times
850                        // for various targets which are either specified in the manifest
851                        // or on the cargo command-line.
852                        let lib_fk = if fk == FeaturesFor::default() {
853                            (self.track_for_host && (dep.is_build() || self.has_proc_macro_lib(dep_id)))
854                                .then(|| FeaturesFor::HostDep)
855                                .unwrap_or_default()
856                        } else {
857                            fk
858                        };
859
860                        // `artifact_target_keys` are produced to fulfil the needs of artifacts that have a target specification.
861                        let artifact_target_keys = dep
862                            .artifact()
863                            .map(|artifact| {
864                                let host_triple = self.target_data.rustc.host;
865                                // not all targets may be queried before resolution since artifact dependencies
866                                // and per-pkg-targets are not immediately known.
867                                let mut activate_target = |target| {
868                                    let name = dep.name_in_toml();
869                                    self.target_data
870                                        .merge_compile_kind(CompileKind::Target(target))
871                                        .with_context(|| format!("failed to determine target information for target `{target}`.\n  \
872                                        Artifact dependency `{name}` in package `{pkg_id}` requires building for `{target}`", target = target.rustc_target()))
873                                };
874                                CargoResult::Ok((
875                                    artifact.is_lib(),
876                                    artifact
877                                        .target()
878                                        .map(|target| {
879                                            CargoResult::Ok(match target {
880                                                ArtifactTarget::Force(target) => {
881                                                    activate_target(target)?;
882                                                    vec![FeaturesFor::ArtifactDep(target)]
883                                                }
884                                                // FIXME: this needs to interact with the `default-target` and `forced-target` values
885                                                // of the dependency
886                                                ArtifactTarget::BuildDependencyAssumeTarget => self
887                                                    .requested_targets
888                                                    .iter()
889                                                    .map(|kind| match kind {
890                                                        CompileKind::Host => {
891                                                            CompileTarget::new(&host_triple)
892                                                                .unwrap()
893                                                        }
894                                                        CompileKind::Target(target) => *target,
895                                                    })
896                                                    .map(|target| {
897                                                        activate_target(target)?;
898                                                        Ok(FeaturesFor::ArtifactDep(target))
899                                                    })
900                                                    .collect::<CargoResult<_>>()?,
901                                            })
902                                        })
903                                        .transpose()?,
904                                ))
905                            })
906                            .transpose()?;
907
908                        let dep_fks = match artifact_target_keys {
909                            // The artifact is also a library and does specify custom
910                            // targets.
911                            // The library's feature key needs to be used alongside
912                            // the keys artifact targets.
913                            Some((is_lib, Some(mut dep_fks))) if is_lib => {
914                                dep_fks.push(lib_fk);
915                                dep_fks
916                            }
917                            // The artifact is not a library, but does specify
918                            // custom targets.
919                            // Use only these targets feature keys.
920                            Some((_, Some(dep_fks))) => dep_fks,
921                            // There is no artifact in the current dependency
922                            // or there is no target specified on the artifact.
923                            // Use the standard feature key without any alteration.
924                            Some((_, None)) | None => vec![lib_fk],
925                        };
926                        Ok(dep_fks.into_iter().map(move |dep_fk| (dep, dep_fk)))
927                    })
928                    .flatten_ok()
929                    .collect::<CargoResult<Vec<_>>>()?;
930                Ok((dep_id, deps))
931            })
932            .filter(|res| res.as_ref().map_or(true, |(_id, deps)| !deps.is_empty()))
933            .collect()
934    }
935
936    /// Compare the activated features to the resolver. Used for testing.
937    fn compare(&self) {
938        let mut found = false;
939        for ((pkg_id, dep_kind), features) in &self.activated_features {
940            let r_features = self.resolve.features(*pkg_id);
941            if !r_features.iter().eq(features.iter()) {
942                crate::drop_eprintln!(
943                    self.ws.gctx(),
944                    "{}/{:?} features mismatch\nresolve: {:?}\nnew: {:?}\n",
945                    pkg_id,
946                    dep_kind,
947                    r_features,
948                    features
949                );
950                found = true;
951            }
952        }
953        if found {
954            panic!("feature mismatch");
955        }
956    }
957
958    /// Whether the given package has any proc macro target, including proc-macro examples.
959    fn has_any_proc_macro(&self, package_id: PackageId) -> bool {
960        self.package_set
961            .get_one(package_id)
962            .expect("packages downloaded")
963            .proc_macro()
964    }
965
966    /// Whether the given package is a proc macro lib target.
967    ///
968    /// This is useful for checking if a dependency is a proc macro,
969    /// as it is not possible to depend on a non-lib target as a proc-macro.
970    fn has_proc_macro_lib(&self, package_id: PackageId) -> bool {
971        self.package_set
972            .get_one(package_id)
973            .expect("packages downloaded")
974            .library()
975            .map(|lib| lib.proc_macro())
976            .unwrap_or_default()
977    }
978}