]> git.proxmox.com Git - cargo.git/blob - src/cargo/core/resolver/features.rs
eb58c391a2b8181bb16ae5ff1542f6425be394d2
[cargo.git] / src / cargo / core / resolver / features.rs
1 //! Feature resolver.
2 //!
3 //! This is a new feature resolver that runs independently of the main
4 //! dependency resolver. It is enabled when the user specifies `resolver =
5 //! "2"` in `Cargo.toml`.
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. This can be verified by setting the
12 //! `__CARGO_FORCE_NEW_FEATURES=compare` environment variable and running
13 //! Cargo's test suite (or building other projects), and checking if it
14 //! panics. Note: the `features2` tests will fail because they intentionally
15 //! compare the old vs new behavior, so forcing the old behavior will
16 //! naturally fail the tests.
17 //!
18 //! The preferred way to engage this new resolver is via
19 //! `resolve_ws_with_opts`.
20 //!
21 //! This does not *replace* feature resolution in the dependency resolver, but
22 //! instead acts as a second pass which can *narrow* the features selected in
23 //! the dependency resolver. The dependency resolver still needs to do its own
24 //! feature resolution in order to avoid selecting optional dependencies that
25 //! are never enabled. The dependency resolver could, in theory, just assume
26 //! all optional dependencies on all packages are enabled (and remove all
27 //! knowledge of features), but that could introduce new requirements that
28 //! might change old behavior or cause conflicts. Maybe some day in the future
29 //! we could experiment with that, but it seems unlikely to work or be all
30 //! that helpful.
31 //!
32 //! There are many assumptions made about the dependency resolver. This
33 //! feature resolver assumes validation has already been done on the feature
34 //! maps, and doesn't do any validation itself. It assumes dev-dependencies
35 //! within a dependency have been removed. There are probably other
36 //! assumptions that I am forgetting.
37
38 use crate::core::compiler::{CompileKind, RustcTargetData};
39 use crate::core::dependency::{DepKind, Dependency};
40 use crate::core::resolver::types::FeaturesSet;
41 use crate::core::resolver::{Resolve, ResolveBehavior};
42 use crate::core::{FeatureValue, PackageId, PackageIdSpec, PackageSet, Workspace};
43 use crate::util::interning::InternedString;
44 use crate::util::CargoResult;
45 use anyhow::bail;
46 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
47 use std::rc::Rc;
48
49 /// Map of activated features.
50 ///
51 /// The key is `(PackageId, bool)` where the bool is `true` if these
52 /// are features for a build dependency or proc-macro.
53 type ActivateMap = HashMap<(PackageId, bool), BTreeSet<InternedString>>;
54
55 /// Set of all activated features for all packages in the resolve graph.
56 pub struct ResolvedFeatures {
57 activated_features: ActivateMap,
58 /// Optional dependencies that should be built.
59 ///
60 /// The value is the `name_in_toml` of the dependencies.
61 activated_dependencies: ActivateMap,
62 /// This is only here for legacy support when the new resolver is not enabled.
63 ///
64 /// This is the set of features enabled for each package.
65 legacy_features: Option<HashMap<PackageId, Vec<InternedString>>>,
66 /// This is only here for legacy support when the new resolver is not enabled.
67 ///
68 /// This is the set of optional dependencies enabled for each package.
69 legacy_dependencies: Option<HashMap<PackageId, HashSet<InternedString>>>,
70 opts: FeatureOpts,
71 }
72
73 /// Options for how the feature resolver works.
74 #[derive(Default)]
75 pub struct FeatureOpts {
76 /// Use the new resolver instead of the old one.
77 new_resolver: bool,
78 /// Build deps and proc-macros will not share share features with other dep kinds.
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)]
96 pub enum HasDevUnits {
97 Yes,
98 No,
99 }
100
101 /// Flag to indicate that target-specific filtering should be disabled.
102 #[derive(Copy, Clone, PartialEq)]
103 pub enum ForceAllTargets {
104 Yes,
105 No,
106 }
107
108 /// Flag to indicate if features are requested for a build dependency or not.
109 #[derive(Copy, Clone, Debug, PartialEq)]
110 pub enum FeaturesFor {
111 NormalOrDev,
112 /// Build dependency or proc-macro.
113 HostDep,
114 }
115
116 impl FeaturesFor {
117 pub fn from_for_host(for_host: bool) -> FeaturesFor {
118 if for_host {
119 FeaturesFor::HostDep
120 } else {
121 FeaturesFor::NormalOrDev
122 }
123 }
124 }
125
126 impl FeatureOpts {
127 pub fn new(
128 ws: &Workspace<'_>,
129 has_dev_units: HasDevUnits,
130 force_all_targets: ForceAllTargets,
131 ) -> CargoResult<FeatureOpts> {
132 let mut opts = FeatureOpts::default();
133 let unstable_flags = ws.config().cli_unstable();
134 let mut enable = |feat_opts: &Vec<String>| {
135 opts.new_resolver = true;
136 for opt in feat_opts {
137 match opt.as_ref() {
138 "build_dep" | "host_dep" => opts.decouple_host_deps = true,
139 "dev_dep" => opts.decouple_dev_deps = true,
140 "itarget" => opts.ignore_inactive_targets = true,
141 "all" => {
142 opts.decouple_host_deps = true;
143 opts.decouple_dev_deps = true;
144 opts.ignore_inactive_targets = true;
145 }
146 "compare" => opts.compare = true,
147 "ws" => unimplemented!(),
148 s => bail!("-Zfeatures flag `{}` is not supported", s),
149 }
150 }
151 Ok(())
152 };
153 if let Some(feat_opts) = unstable_flags.features.as_ref() {
154 enable(feat_opts)?;
155 }
156 match ws.resolve_behavior() {
157 ResolveBehavior::V1 => {}
158 ResolveBehavior::V2 => {
159 enable(&vec!["all".to_string()]).unwrap();
160 }
161 }
162 // This env var is intended for testing only.
163 if let Ok(env_opts) = std::env::var("__CARGO_FORCE_NEW_FEATURES") {
164 if env_opts == "1" {
165 opts.new_resolver = true;
166 } else {
167 let env_opts = env_opts.split(',').map(|s| s.to_string()).collect();
168 enable(&env_opts)?;
169 }
170 }
171 if let HasDevUnits::Yes = has_dev_units {
172 // Dev deps cannot be decoupled when they are in use.
173 opts.decouple_dev_deps = false;
174 }
175 if let ForceAllTargets::Yes = force_all_targets {
176 opts.ignore_inactive_targets = false;
177 }
178 if unstable_flags.weak_dep_features {
179 // Force this ON because it only works with the new resolver.
180 opts.new_resolver = true;
181 }
182 Ok(opts)
183 }
184
185 /// Creates a new FeatureOpts for the given behavior.
186 pub fn new_behavior(behavior: ResolveBehavior, has_dev_units: HasDevUnits) -> FeatureOpts {
187 match behavior {
188 ResolveBehavior::V1 => FeatureOpts::default(),
189 ResolveBehavior::V2 => FeatureOpts {
190 new_resolver: true,
191 decouple_host_deps: true,
192 decouple_dev_deps: has_dev_units == HasDevUnits::No,
193 ignore_inactive_targets: true,
194 compare: false,
195 },
196 }
197 }
198 }
199
200 /// Features flags requested for a package.
201 ///
202 /// This should be cheap and fast to clone, it is used in the resolver for
203 /// various caches.
204 ///
205 /// This is split into enum variants because the resolver needs to handle
206 /// features coming from different places (command-line and dependency
207 /// declarations), but those different places have different constraints on
208 /// which syntax is allowed. This helps ensure that every place dealing with
209 /// features is properly handling those syntax restrictions.
210 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
211 pub enum RequestedFeatures {
212 /// Features requested on the command-line with flags.
213 CliFeatures(CliFeatures),
214 /// Features specified in a dependency declaration.
215 DepFeatures {
216 /// The `features` dependency field.
217 features: FeaturesSet,
218 /// The `default-features` dependency field.
219 uses_default_features: bool,
220 },
221 }
222
223 /// Features specified on the command-line.
224 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
225 pub struct CliFeatures {
226 /// Features from the `--features` flag.
227 pub features: Rc<BTreeSet<FeatureValue>>,
228 /// The `--all-features` flag.
229 pub all_features: bool,
230 /// Inverse of `--no-default-features` flag.
231 pub uses_default_features: bool,
232 }
233
234 impl CliFeatures {
235 /// Creates a new CliFeatures from the given command-line flags.
236 pub fn from_command_line(
237 features: &[String],
238 all_features: bool,
239 uses_default_features: bool,
240 ) -> CargoResult<CliFeatures> {
241 let features = Rc::new(CliFeatures::split_features(features));
242 // Some early validation to ensure correct syntax.
243 for feature in features.iter() {
244 match feature {
245 // Maybe call validate_feature_name here once it is an error?
246 FeatureValue::Feature(_) => {}
247 FeatureValue::Dep { .. }
248 | FeatureValue::DepFeature {
249 dep_prefix: true, ..
250 } => {
251 bail!(
252 "feature `{}` is not allowed to use explicit `dep:` syntax",
253 feature
254 );
255 }
256 FeatureValue::DepFeature { dep_feature, .. } => {
257 if dep_feature.contains('/') {
258 bail!("multiple slashes in feature `{}` is not allowed", feature);
259 }
260 }
261 }
262 }
263 Ok(CliFeatures {
264 features,
265 all_features,
266 uses_default_features,
267 })
268 }
269
270 /// Creates a new CliFeatures with the given `all_features` setting.
271 pub fn new_all(all_features: bool) -> CliFeatures {
272 CliFeatures {
273 features: Rc::new(BTreeSet::new()),
274 all_features,
275 uses_default_features: true,
276 }
277 }
278
279 fn split_features(features: &[String]) -> BTreeSet<FeatureValue> {
280 features
281 .iter()
282 .flat_map(|s| s.split_whitespace())
283 .flat_map(|s| s.split(','))
284 .filter(|s| !s.is_empty())
285 .map(InternedString::new)
286 .map(FeatureValue::new)
287 .collect()
288 }
289 }
290
291 impl ResolvedFeatures {
292 /// Returns the list of features that are enabled for the given package.
293 pub fn activated_features(
294 &self,
295 pkg_id: PackageId,
296 features_for: FeaturesFor,
297 ) -> Vec<InternedString> {
298 self.activated_features_int(pkg_id, features_for)
299 .expect("activated_features for invalid package")
300 }
301
302 /// Returns if the given dependency should be included.
303 ///
304 /// This handles dependencies disabled via `cfg` expressions and optional
305 /// dependencies which are not enabled.
306 pub fn is_dep_activated(
307 &self,
308 pkg_id: PackageId,
309 features_for: FeaturesFor,
310 dep_name: InternedString,
311 ) -> bool {
312 if let Some(legacy) = &self.legacy_dependencies {
313 legacy
314 .get(&pkg_id)
315 .map(|deps| deps.contains(&dep_name))
316 .unwrap_or(false)
317 } else {
318 let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep;
319 self.activated_dependencies
320 .get(&(pkg_id, is_build))
321 .map(|deps| deps.contains(&dep_name))
322 .unwrap_or(false)
323 }
324 }
325
326 /// Variant of `activated_features` that returns `None` if this is
327 /// not a valid pkg_id/is_build combination. Used in places which do
328 /// not know which packages are activated (like `cargo clean`).
329 pub fn activated_features_unverified(
330 &self,
331 pkg_id: PackageId,
332 features_for: FeaturesFor,
333 ) -> Option<Vec<InternedString>> {
334 self.activated_features_int(pkg_id, features_for).ok()
335 }
336
337 fn activated_features_int(
338 &self,
339 pkg_id: PackageId,
340 features_for: FeaturesFor,
341 ) -> CargoResult<Vec<InternedString>> {
342 if let Some(legacy) = &self.legacy_features {
343 Ok(legacy.get(&pkg_id).map_or_else(Vec::new, |v| v.clone()))
344 } else {
345 let is_build = self.opts.decouple_host_deps && features_for == FeaturesFor::HostDep;
346 if let Some(fs) = self.activated_features.get(&(pkg_id, is_build)) {
347 Ok(fs.iter().cloned().collect())
348 } else {
349 bail!("features did not find {:?} {:?}", pkg_id, is_build)
350 }
351 }
352 }
353
354 /// Compares the result against the original resolver behavior.
355 ///
356 /// Used by `cargo fix --edition` to display any differences.
357 pub fn compare_legacy(&self, legacy: &ResolvedFeatures) -> FeatureDifferences {
358 let legacy_features = legacy.legacy_features.as_ref().unwrap();
359 let features = self
360 .activated_features
361 .iter()
362 .filter_map(|((pkg_id, for_host), new_features)| {
363 let old_features = match legacy_features.get(pkg_id) {
364 Some(feats) => feats.iter().cloned().collect(),
365 None => BTreeSet::new(),
366 };
367 // The new resolver should never add features.
368 assert_eq!(new_features.difference(&old_features).next(), None);
369 let removed_features: BTreeSet<_> =
370 old_features.difference(new_features).cloned().collect();
371 if removed_features.is_empty() {
372 None
373 } else {
374 Some(((*pkg_id, *for_host), removed_features))
375 }
376 })
377 .collect();
378 let legacy_deps = legacy.legacy_dependencies.as_ref().unwrap();
379 let optional_deps = self
380 .activated_dependencies
381 .iter()
382 .filter_map(|((pkg_id, for_host), new_deps)| {
383 let old_deps = match legacy_deps.get(pkg_id) {
384 Some(deps) => deps.iter().cloned().collect(),
385 None => BTreeSet::new(),
386 };
387 // The new resolver should never add dependencies.
388 assert_eq!(new_deps.difference(&old_deps).next(), None);
389 let removed_deps: BTreeSet<_> = old_deps.difference(new_deps).cloned().collect();
390 if removed_deps.is_empty() {
391 None
392 } else {
393 Some(((*pkg_id, *for_host), removed_deps))
394 }
395 })
396 .collect();
397 FeatureDifferences {
398 features,
399 optional_deps,
400 }
401 }
402 }
403
404 /// Map of differences.
405 ///
406 /// Key is `(pkg_id, for_host)`. Value is a set of features or dependencies removed.
407 pub type DiffMap = BTreeMap<(PackageId, bool), BTreeSet<InternedString>>;
408
409 /// Differences between resolvers.
410 pub struct FeatureDifferences {
411 pub features: DiffMap,
412 pub optional_deps: DiffMap,
413 }
414
415 pub struct FeatureResolver<'a, 'cfg> {
416 ws: &'a Workspace<'cfg>,
417 target_data: &'a RustcTargetData<'cfg>,
418 /// The platforms to build for, requested by the user.
419 requested_targets: &'a [CompileKind],
420 resolve: &'a Resolve,
421 package_set: &'a PackageSet<'cfg>,
422 /// Options that change how the feature resolver operates.
423 opts: FeatureOpts,
424 /// Map of features activated for each package.
425 activated_features: ActivateMap,
426 /// Map of optional dependencies activated for each package.
427 activated_dependencies: ActivateMap,
428 /// Keeps track of which packages have had its dependencies processed.
429 /// Used to avoid cycles, and to speed up processing.
430 processed_deps: HashSet<(PackageId, bool)>,
431 /// If this is `true`, then `for_host` needs to be tracked while
432 /// traversing the graph.
433 ///
434 /// This is only here to avoid calling `is_proc_macro` when all feature
435 /// options are disabled (because `is_proc_macro` can trigger downloads).
436 /// This has to be separate from `FeatureOpts.decouple_host_deps` because
437 /// `for_host` tracking is also needed for `itarget` to work properly.
438 track_for_host: bool,
439 /// `dep_name?/feat_name` features that will be activated if `dep_name` is
440 /// ever activated.
441 ///
442 /// The key is the `(package, for_host, dep_name)` of the package whose
443 /// dependency will trigger the addition of new features. The value is the
444 /// set of `(feature, dep_prefix)` features to activate (`dep_prefix` is a
445 /// bool that indicates if `dep:` prefix was used).
446 deferred_weak_dependencies:
447 HashMap<(PackageId, bool, InternedString), HashSet<(InternedString, bool)>>,
448 }
449
450 impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
451 /// Runs the resolution algorithm and returns a new `ResolvedFeatures`
452 /// with the result.
453 pub fn resolve(
454 ws: &Workspace<'cfg>,
455 target_data: &RustcTargetData<'cfg>,
456 resolve: &Resolve,
457 package_set: &'a PackageSet<'cfg>,
458 cli_features: &CliFeatures,
459 specs: &[PackageIdSpec],
460 requested_targets: &[CompileKind],
461 opts: FeatureOpts,
462 ) -> CargoResult<ResolvedFeatures> {
463 use crate::util::profile;
464 let _p = profile::start("resolve features");
465
466 if !opts.new_resolver {
467 // Legacy mode.
468 return Ok(ResolvedFeatures {
469 activated_features: HashMap::new(),
470 activated_dependencies: HashMap::new(),
471 legacy_features: Some(resolve.features_clone()),
472 legacy_dependencies: Some(compute_legacy_deps(resolve)),
473 opts,
474 });
475 }
476 let track_for_host = opts.decouple_host_deps || opts.ignore_inactive_targets;
477 let mut r = FeatureResolver {
478 ws,
479 target_data,
480 requested_targets,
481 resolve,
482 package_set,
483 opts,
484 activated_features: HashMap::new(),
485 activated_dependencies: HashMap::new(),
486 processed_deps: HashSet::new(),
487 track_for_host,
488 deferred_weak_dependencies: HashMap::new(),
489 };
490 r.do_resolve(specs, cli_features)?;
491 log::debug!("features={:#?}", r.activated_features);
492 if r.opts.compare {
493 r.compare();
494 }
495 Ok(ResolvedFeatures {
496 activated_features: r.activated_features,
497 activated_dependencies: r.activated_dependencies,
498 legacy_features: None,
499 legacy_dependencies: None,
500 opts: r.opts,
501 })
502 }
503
504 /// Performs the process of resolving all features for the resolve graph.
505 fn do_resolve(
506 &mut self,
507 specs: &[PackageIdSpec],
508 cli_features: &CliFeatures,
509 ) -> CargoResult<()> {
510 let member_features = self.ws.members_with_features(specs, cli_features)?;
511 for (member, cli_features) in &member_features {
512 let fvs = self.fvs_from_requested(member.package_id(), cli_features);
513 let for_host = self.track_for_host && self.is_proc_macro(member.package_id());
514 self.activate_pkg(member.package_id(), for_host, &fvs)?;
515 if for_host {
516 // Also activate without for_host. This is needed if the
517 // proc-macro includes other targets (like binaries or tests),
518 // or running in `cargo test`. Note that in a workspace, if
519 // the proc-macro is selected on the command like (like with
520 // `--workspace`), this forces feature unification with normal
521 // dependencies. This is part of the bigger problem where
522 // features depend on which packages are built.
523 self.activate_pkg(member.package_id(), false, &fvs)?;
524 }
525 }
526 Ok(())
527 }
528
529 fn activate_pkg(
530 &mut self,
531 pkg_id: PackageId,
532 for_host: bool,
533 fvs: &[FeatureValue],
534 ) -> CargoResult<()> {
535 log::trace!("activate_pkg {} {}", pkg_id.name(), for_host);
536 // Add an empty entry to ensure everything is covered. This is intended for
537 // finding bugs where the resolver missed something it should have visited.
538 // Remove this in the future if `activated_features` uses an empty default.
539 self.activated_features
540 .entry((pkg_id, self.opts.decouple_host_deps && for_host))
541 .or_insert_with(BTreeSet::new);
542 for fv in fvs {
543 self.activate_fv(pkg_id, for_host, fv)?;
544 }
545 if !self.processed_deps.insert((pkg_id, for_host)) {
546 // Already processed dependencies. There's no need to process them
547 // again. This is primarily to avoid cycles, but also helps speed
548 // things up.
549 //
550 // This is safe because if another package comes along and adds a
551 // feature on this package, it will immediately add it (in
552 // `activate_fv`), and recurse as necessary right then and there.
553 // For example, consider we've already processed our dependencies,
554 // and another package comes along and enables one of our optional
555 // dependencies, it will do so immediately in the
556 // `FeatureValue::DepFeature` branch, and then immediately
557 // recurse into that optional dependency. This also holds true for
558 // features that enable other features.
559 return Ok(());
560 }
561 for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
562 for (dep, dep_for_host) in deps {
563 if dep.is_optional() {
564 // Optional dependencies are enabled in `activate_fv` when
565 // a feature enables it.
566 continue;
567 }
568 // Recurse into the dependency.
569 let fvs = self.fvs_from_dependency(dep_pkg_id, dep);
570 self.activate_pkg(dep_pkg_id, dep_for_host, &fvs)?;
571 }
572 }
573 Ok(())
574 }
575
576 /// Activate a single FeatureValue for a package.
577 fn activate_fv(
578 &mut self,
579 pkg_id: PackageId,
580 for_host: bool,
581 fv: &FeatureValue,
582 ) -> CargoResult<()> {
583 log::trace!("activate_fv {} {} {}", pkg_id.name(), for_host, fv);
584 match fv {
585 FeatureValue::Feature(f) => {
586 self.activate_rec(pkg_id, for_host, *f)?;
587 }
588 FeatureValue::Dep { dep_name } => {
589 self.activate_dependency(pkg_id, for_host, *dep_name)?;
590 }
591 FeatureValue::DepFeature {
592 dep_name,
593 dep_feature,
594 dep_prefix,
595 weak,
596 } => {
597 self.activate_dep_feature(
598 pkg_id,
599 for_host,
600 *dep_name,
601 *dep_feature,
602 *dep_prefix,
603 *weak,
604 )?;
605 }
606 }
607 Ok(())
608 }
609
610 /// Activate the given feature for the given package, and then recursively
611 /// activate any other features that feature enables.
612 fn activate_rec(
613 &mut self,
614 pkg_id: PackageId,
615 for_host: bool,
616 feature_to_enable: InternedString,
617 ) -> CargoResult<()> {
618 log::trace!(
619 "activate_rec {} {} feat={}",
620 pkg_id.name(),
621 for_host,
622 feature_to_enable
623 );
624 let enabled = self
625 .activated_features
626 .entry((pkg_id, self.opts.decouple_host_deps && for_host))
627 .or_insert_with(BTreeSet::new);
628 if !enabled.insert(feature_to_enable) {
629 // Already enabled.
630 return Ok(());
631 }
632 let summary = self.resolve.summary(pkg_id);
633 let feature_map = summary.features();
634 let fvs = match feature_map.get(&feature_to_enable) {
635 Some(fvs) => fvs,
636 None => {
637 // TODO: this should only happen for optional dependencies.
638 // Other cases should be validated by Summary's `build_feature_map`.
639 // Figure out some way to validate this assumption.
640 log::debug!(
641 "pkg {:?} does not define feature {}",
642 pkg_id,
643 feature_to_enable
644 );
645 return Ok(());
646 }
647 };
648 for fv in fvs {
649 self.activate_fv(pkg_id, for_host, fv)?;
650 }
651 Ok(())
652 }
653
654 /// Activate a dependency (`dep:dep_name` syntax).
655 fn activate_dependency(
656 &mut self,
657 pkg_id: PackageId,
658 for_host: bool,
659 dep_name: InternedString,
660 ) -> CargoResult<()> {
661 // Mark this dependency as activated.
662 let save_for_host = self.opts.decouple_host_deps && for_host;
663 self.activated_dependencies
664 .entry((pkg_id, save_for_host))
665 .or_default()
666 .insert(dep_name);
667 // Check for any deferred features.
668 let to_enable = self
669 .deferred_weak_dependencies
670 .remove(&(pkg_id, for_host, dep_name));
671 // Activate the optional dep.
672 for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
673 for (dep, dep_for_host) in deps {
674 if dep.name_in_toml() != dep_name {
675 continue;
676 }
677 if let Some(to_enable) = &to_enable {
678 for (dep_feature, dep_prefix) in to_enable {
679 log::trace!(
680 "activate deferred {} {} -> {}/{}",
681 pkg_id.name(),
682 for_host,
683 dep_name,
684 dep_feature
685 );
686 if !dep_prefix {
687 self.activate_rec(pkg_id, for_host, dep_name)?;
688 }
689 let fv = FeatureValue::new(*dep_feature);
690 self.activate_fv(dep_pkg_id, dep_for_host, &fv)?;
691 }
692 }
693 let fvs = self.fvs_from_dependency(dep_pkg_id, dep);
694 self.activate_pkg(dep_pkg_id, dep_for_host, &fvs)?;
695 }
696 }
697 Ok(())
698 }
699
700 /// Activate a feature within a dependency (`dep_name/feat_name` syntax).
701 fn activate_dep_feature(
702 &mut self,
703 pkg_id: PackageId,
704 for_host: bool,
705 dep_name: InternedString,
706 dep_feature: InternedString,
707 dep_prefix: bool,
708 weak: bool,
709 ) -> CargoResult<()> {
710 for (dep_pkg_id, deps) in self.deps(pkg_id, for_host) {
711 for (dep, dep_for_host) in deps {
712 if dep.name_in_toml() != dep_name {
713 continue;
714 }
715 if dep.is_optional() {
716 let save_for_host = self.opts.decouple_host_deps && for_host;
717 if weak
718 && !self
719 .activated_dependencies
720 .get(&(pkg_id, save_for_host))
721 .map(|deps| deps.contains(&dep_name))
722 .unwrap_or(false)
723 {
724 // This is weak, but not yet activated. Defer in case
725 // something comes along later and enables it.
726 log::trace!(
727 "deferring feature {} {} -> {}/{}",
728 pkg_id.name(),
729 for_host,
730 dep_name,
731 dep_feature
732 );
733 self.deferred_weak_dependencies
734 .entry((pkg_id, for_host, dep_name))
735 .or_default()
736 .insert((dep_feature, dep_prefix));
737 continue;
738 }
739
740 // Activate the dependency on self.
741 let fv = FeatureValue::Dep { dep_name };
742 self.activate_fv(pkg_id, for_host, &fv)?;
743 if !dep_prefix {
744 // To retain compatibility with old behavior,
745 // this also enables a feature of the same
746 // name.
747 self.activate_rec(pkg_id, for_host, dep_name)?;
748 }
749 }
750 // Activate the feature on the dependency.
751 let fv = FeatureValue::new(dep_feature);
752 self.activate_fv(dep_pkg_id, dep_for_host, &fv)?;
753 }
754 }
755 Ok(())
756 }
757
758 /// Returns Vec of FeatureValues from a Dependency definition.
759 fn fvs_from_dependency(&self, dep_id: PackageId, dep: &Dependency) -> Vec<FeatureValue> {
760 let summary = self.resolve.summary(dep_id);
761 let feature_map = summary.features();
762 let mut result: Vec<FeatureValue> = dep
763 .features()
764 .iter()
765 .map(|f| FeatureValue::new(*f))
766 .collect();
767 let default = InternedString::new("default");
768 if dep.uses_default_features() && feature_map.contains_key(&default) {
769 result.push(FeatureValue::Feature(default));
770 }
771 result
772 }
773
774 /// Returns Vec of FeatureValues from a set of command-line features.
775 fn fvs_from_requested(
776 &self,
777 pkg_id: PackageId,
778 cli_features: &CliFeatures,
779 ) -> Vec<FeatureValue> {
780 let summary = self.resolve.summary(pkg_id);
781 let feature_map = summary.features();
782 if cli_features.all_features {
783 feature_map
784 .keys()
785 .map(|k| FeatureValue::Feature(*k))
786 .collect()
787 } else {
788 let mut result: Vec<FeatureValue> = cli_features.features.iter().cloned().collect();
789 let default = InternedString::new("default");
790 if cli_features.uses_default_features && feature_map.contains_key(&default) {
791 result.push(FeatureValue::Feature(default));
792 }
793 result
794 }
795 }
796
797 /// Returns the dependencies for a package, filtering out inactive targets.
798 fn deps(
799 &self,
800 pkg_id: PackageId,
801 for_host: bool,
802 ) -> Vec<(PackageId, Vec<(&'a Dependency, bool)>)> {
803 // Helper for determining if a platform is activated.
804 let platform_activated = |dep: &Dependency| -> bool {
805 // We always care about build-dependencies, and they are always
806 // Host. If we are computing dependencies "for a build script",
807 // even normal dependencies are host-only.
808 if for_host || dep.is_build() {
809 return self
810 .target_data
811 .dep_platform_activated(dep, CompileKind::Host);
812 }
813 // Not a build dependency, and not for a build script, so must be Target.
814 self.requested_targets
815 .iter()
816 .any(|kind| self.target_data.dep_platform_activated(dep, *kind))
817 };
818 self.resolve
819 .deps(pkg_id)
820 .map(|(dep_id, deps)| {
821 let deps = deps
822 .iter()
823 .filter(|dep| {
824 if dep.platform().is_some()
825 && self.opts.ignore_inactive_targets
826 && !platform_activated(dep)
827 {
828 return false;
829 }
830 if self.opts.decouple_dev_deps && dep.kind() == DepKind::Development {
831 return false;
832 }
833 true
834 })
835 .map(|dep| {
836 let dep_for_host = self.track_for_host
837 && (for_host || dep.is_build() || self.is_proc_macro(dep_id));
838 (dep, dep_for_host)
839 })
840 .collect::<Vec<_>>();
841 (dep_id, deps)
842 })
843 .filter(|(_id, deps)| !deps.is_empty())
844 .collect()
845 }
846
847 /// Compare the activated features to the resolver. Used for testing.
848 fn compare(&self) {
849 let mut found = false;
850 for ((pkg_id, dep_kind), features) in &self.activated_features {
851 let r_features = self.resolve.features(*pkg_id);
852 if !r_features.iter().eq(features.iter()) {
853 crate::drop_eprintln!(
854 self.ws.config(),
855 "{}/{:?} features mismatch\nresolve: {:?}\nnew: {:?}\n",
856 pkg_id,
857 dep_kind,
858 r_features,
859 features
860 );
861 found = true;
862 }
863 }
864 if found {
865 panic!("feature mismatch");
866 }
867 }
868
869 fn is_proc_macro(&self, package_id: PackageId) -> bool {
870 self.package_set
871 .get_one(package_id)
872 .expect("packages downloaded")
873 .proc_macro()
874 }
875 }
876
877 /// Computes a map of PackageId to the set of optional dependencies that are
878 /// enabled for that dep (when the new resolver is not enabled).
879 fn compute_legacy_deps(resolve: &Resolve) -> HashMap<PackageId, HashSet<InternedString>> {
880 let mut result: HashMap<PackageId, HashSet<InternedString>> = HashMap::new();
881 for pkg_id in resolve.iter() {
882 for (_dep_id, deps) in resolve.deps(pkg_id) {
883 for dep in deps {
884 if dep.is_optional() {
885 result.entry(pkg_id).or_default().insert(dep.name_in_toml());
886 }
887 }
888 }
889 }
890 result
891 }