Skip to main content

cargo/core/resolver/
dep_cache.rs

1//! There are 2 sources of facts for the resolver:
2//!
3//! - The `Registry` tells us for a `Dependency` what versions are available to fulfil it.
4//! - The `Summary` tells us for a version (and features) what dependencies need to be fulfilled for it to be activated.
5//!
6//! These constitute immutable facts, the soled ground truth that all other inference depends on.
7//! Theoretically this could all be enumerated ahead of time, but we want to be lazy and only
8//! look up things we need to. The compromise is to cache the results as they are computed.
9//!
10//! This module impl that cache in all the gory details
11
12use std::cmp::Ordering;
13use std::collections::{BTreeSet, HashMap, HashSet};
14use std::rc::Rc;
15
16use log::debug;
17
18use crate::core::interning::InternedString;
19use crate::core::resolver::context::Context;
20use crate::core::resolver::errors::describe_path;
21use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary};
22use crate::util::errors::{CargoResult, CargoResultExt};
23
24use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
25use crate::core::resolver::{ActivateResult, ResolveOpts};
26
27pub struct RegistryQueryer<'a> {
28    pub registry: &'a mut (dyn Registry + 'a),
29    replacements: &'a [(PackageIdSpec, Dependency)],
30    try_to_use: &'a HashSet<PackageId>,
31    /// If set the list of dependency candidates will be sorted by minimal
32    /// versions first. That allows `cargo update -Z minimal-versions` which will
33    /// specify minimum dependency versions to be used.
34    minimal_versions: bool,
35    /// a cache of `Candidate`s that fulfil a `Dependency`
36    registry_cache: HashMap<Dependency, Rc<Vec<Summary>>>,
37    /// a cache of `Dependency`s that are required for a `Summary`
38    summary_cache: HashMap<
39        (Option<PackageId>, Summary, ResolveOpts),
40        Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
41    >,
42    /// all the cases we ended up using a supplied replacement
43    used_replacements: HashMap<PackageId, Summary>,
44}
45
46impl<'a> RegistryQueryer<'a> {
47    pub fn new(
48        registry: &'a mut dyn Registry,
49        replacements: &'a [(PackageIdSpec, Dependency)],
50        try_to_use: &'a HashSet<PackageId>,
51        minimal_versions: bool,
52    ) -> Self {
53        RegistryQueryer {
54            registry,
55            replacements,
56            try_to_use,
57            minimal_versions,
58            registry_cache: HashMap::new(),
59            summary_cache: HashMap::new(),
60            used_replacements: HashMap::new(),
61        }
62    }
63
64    pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
65        self.used_replacements.get(&p).map(|r| (p, r.package_id()))
66    }
67
68    pub fn replacement_summary(&self, p: PackageId) -> Option<&Summary> {
69        self.used_replacements.get(&p)
70    }
71
72    /// Queries the `registry` to return a list of candidates for `dep`.
73    ///
74    /// This method is the location where overrides are taken into account. If
75    /// any candidates are returned which match an override then the override is
76    /// applied by performing a second query for what the override should
77    /// return.
78    pub fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Summary>>> {
79        if let Some(out) = self.registry_cache.get(dep).cloned() {
80            return Ok(out);
81        }
82
83        let mut ret = Vec::new();
84        self.registry.query(
85            dep,
86            &mut |s| {
87                ret.push(s);
88            },
89            false,
90        )?;
91        for summary in ret.iter_mut() {
92            let mut potential_matches = self
93                .replacements
94                .iter()
95                .filter(|&&(ref spec, _)| spec.matches(summary.package_id()));
96
97            let &(ref spec, ref dep) = match potential_matches.next() {
98                None => continue,
99                Some(replacement) => replacement,
100            };
101            debug!(
102                "found an override for {} {}",
103                dep.package_name(),
104                dep.version_req()
105            );
106
107            let mut summaries = self.registry.query_vec(dep, false)?.into_iter();
108            let s = summaries.next().ok_or_else(|| {
109                anyhow::format_err!(
110                    "no matching package for override `{}` found\n\
111                     location searched: {}\n\
112                     version required: {}",
113                    spec,
114                    dep.source_id(),
115                    dep.version_req()
116                )
117            })?;
118            let summaries = summaries.collect::<Vec<_>>();
119            if !summaries.is_empty() {
120                let bullets = summaries
121                    .iter()
122                    .map(|s| format!("  * {}", s.package_id()))
123                    .collect::<Vec<_>>();
124                anyhow::bail!(
125                    "the replacement specification `{}` matched \
126                     multiple packages:\n  * {}\n{}",
127                    spec,
128                    s.package_id(),
129                    bullets.join("\n")
130                );
131            }
132
133            // The dependency should be hard-coded to have the same name and an
134            // exact version requirement, so both of these assertions should
135            // never fail.
136            assert_eq!(s.version(), summary.version());
137            assert_eq!(s.name(), summary.name());
138
139            let replace = if s.source_id() == summary.source_id() {
140                debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
141                None
142            } else {
143                Some(s)
144            };
145            let matched_spec = spec.clone();
146
147            // Make sure no duplicates
148            if let Some(&(ref spec, _)) = potential_matches.next() {
149                anyhow::bail!(
150                    "overlapping replacement specifications found:\n\n  \
151                     * {}\n  * {}\n\nboth specifications match: {}",
152                    matched_spec,
153                    spec,
154                    summary.package_id()
155                );
156            }
157
158            for dep in summary.dependencies() {
159                debug!("\t{} => {}", dep.package_name(), dep.version_req());
160            }
161            if let Some(r) = replace {
162                self.used_replacements.insert(summary.package_id(), r);
163            }
164        }
165
166        // When we attempt versions for a package we'll want to do so in a
167        // sorted fashion to pick the "best candidates" first. Currently we try
168        // prioritized summaries (those in `try_to_use`) and failing that we
169        // list everything from the maximum version to the lowest version.
170        ret.sort_unstable_by(|a, b| {
171            let a_in_previous = self.try_to_use.contains(&a.package_id());
172            let b_in_previous = self.try_to_use.contains(&b.package_id());
173            let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
174            match previous_cmp {
175                Ordering::Equal => {
176                    let cmp = a.version().cmp(b.version());
177                    if self.minimal_versions {
178                        // Lower version ordered first.
179                        cmp
180                    } else {
181                        // Higher version ordered first.
182                        cmp.reverse()
183                    }
184                }
185                _ => previous_cmp,
186            }
187        });
188
189        let out = Rc::new(ret);
190
191        self.registry_cache.insert(dep.clone(), out.clone());
192
193        Ok(out)
194    }
195
196    /// Find out what dependencies will be added by activating `candidate`,
197    /// with features described in `opts`. Then look up in the `registry`
198    /// the candidates that will fulfil each of these dependencies, as it is the
199    /// next obvious question.
200    pub fn build_deps(
201        &mut self,
202        cx: &Context,
203        parent: Option<PackageId>,
204        candidate: &Summary,
205        opts: &ResolveOpts,
206    ) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
207        // if we have calculated a result before, then we can just return it,
208        // as it is a "pure" query of its arguments.
209        if let Some(out) = self
210            .summary_cache
211            .get(&(parent, candidate.clone(), opts.clone()))
212            .cloned()
213        {
214            return Ok(out);
215        }
216        // First, figure out our set of dependencies based on the requested set
217        // of features. This also calculates what features we're going to enable
218        // for our own dependencies.
219        let (used_features, deps) = resolve_features(parent, candidate, opts)?;
220
221        // Next, transform all dependencies into a list of possible candidates
222        // which can satisfy that dependency.
223        let mut deps = deps
224            .into_iter()
225            .map(|(dep, features)| {
226                let candidates = self.query(&dep).chain_err(|| {
227                    anyhow::format_err!(
228                        "failed to get `{}` as a dependency of {}",
229                        dep.package_name(),
230                        describe_path(&cx.parents.path_to_bottom(&candidate.package_id())),
231                    )
232                })?;
233                Ok((dep, candidates, features))
234            })
235            .collect::<CargoResult<Vec<DepInfo>>>()?;
236
237        // Attempt to resolve dependencies with fewer candidates before trying
238        // dependencies with more candidates. This way if the dependency with
239        // only one candidate can't be resolved we don't have to do a bunch of
240        // work before we figure that out.
241        deps.sort_by_key(|&(_, ref a, _)| a.len());
242
243        let out = Rc::new((used_features, Rc::new(deps)));
244
245        // If we succeed we add the result to the cache so we can use it again next time.
246        // We don't cache the failure cases as they don't impl Clone.
247        self.summary_cache
248            .insert((parent, candidate.clone(), opts.clone()), out.clone());
249
250        Ok(out)
251    }
252}
253
254/// Returns the features we ended up using and
255/// all dependencies and the features we want from each of them.
256pub fn resolve_features<'b>(
257    parent: Option<PackageId>,
258    s: &'b Summary,
259    opts: &'b ResolveOpts,
260) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
261    // First, filter by dev-dependencies.
262    let deps = s.dependencies();
263    let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
264
265    let reqs = build_requirements(s, opts)?;
266    let mut ret = Vec::new();
267    let mut used_features = HashSet::new();
268    let default_dep = (false, BTreeSet::new());
269
270    // Next, collect all actually enabled dependencies and their features.
271    for dep in deps {
272        // Skip optional dependencies, but not those enabled through a
273        // feature
274        if dep.is_optional() && !reqs.deps.contains_key(&dep.name_in_toml()) {
275            continue;
276        }
277        // So we want this dependency. Move the features we want from
278        // `feature_deps` to `ret` and register ourselves as using this
279        // name.
280        let base = reqs.deps.get(&dep.name_in_toml()).unwrap_or(&default_dep);
281        used_features.insert(dep.name_in_toml());
282        let always_required = !dep.is_optional()
283            && !s
284                .dependencies()
285                .iter()
286                .any(|d| d.is_optional() && d.name_in_toml() == dep.name_in_toml());
287        if always_required && base.0 {
288            return Err(match parent {
289                None => anyhow::format_err!(
290                    "Package `{}` does not have feature `{}`. It has a required dependency \
291                     with that name, but only optional dependencies can be used as features.",
292                    s.package_id(),
293                    dep.name_in_toml()
294                )
295                .into(),
296                Some(p) => (
297                    p,
298                    ConflictReason::RequiredDependencyAsFeatures(dep.name_in_toml()),
299                )
300                    .into(),
301            });
302        }
303        let mut base = base.1.clone();
304        base.extend(dep.features().iter());
305        for feature in base.iter() {
306            if feature.contains('/') {
307                return Err(anyhow::format_err!(
308                    "feature names may not contain slashes: `{}`",
309                    feature
310                )
311                .into());
312            }
313        }
314        ret.push((dep.clone(), Rc::new(base)));
315    }
316
317    // Any entries in `reqs.dep` which weren't used are bugs in that the
318    // package does not actually have those dependencies. We classified
319    // them as dependencies in the first place because there is no such
320    // feature, either.
321    let remaining = reqs
322        .deps
323        .keys()
324        .cloned()
325        .filter(|s| !used_features.contains(s))
326        .collect::<Vec<_>>();
327    if !remaining.is_empty() {
328        let features = remaining.join(", ");
329        return Err(match parent {
330            None => anyhow::format_err!(
331                "Package `{}` does not have these features: `{}`",
332                s.package_id(),
333                features
334            )
335            .into(),
336            Some(p) => (p, ConflictReason::MissingFeatures(features)).into(),
337        });
338    }
339
340    Ok((reqs.into_used(), ret))
341}
342
343/// Takes requested features for a single package from the input `ResolveOpts` and
344/// recurses to find all requested features, dependencies and requested
345/// dependency features in a `Requirements` object, returning it to the resolver.
346fn build_requirements<'a, 'b: 'a>(
347    s: &'a Summary,
348    opts: &'b ResolveOpts,
349) -> CargoResult<Requirements<'a>> {
350    let mut reqs = Requirements::new(s);
351
352    if opts.features.all_features {
353        for key in s.features().keys() {
354            reqs.require_feature(*key)?;
355        }
356        for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
357            reqs.require_dependency(dep.name_in_toml());
358        }
359    } else {
360        for &f in opts.features.features.iter() {
361            reqs.require_value(&FeatureValue::new(f, s))?;
362        }
363    }
364
365    if opts.features.uses_default_features && s.features().contains_key("default") {
366        reqs.require_feature(InternedString::new("default"))?;
367    }
368
369    Ok(reqs)
370}
371
372struct Requirements<'a> {
373    summary: &'a Summary,
374    // The deps map is a mapping of package name to list of features enabled.
375    // Each package should be enabled, and each package should have the
376    // specified set of features enabled. The boolean indicates whether this
377    // package was specifically requested (rather than just requesting features
378    // *within* this package).
379    deps: HashMap<InternedString, (bool, BTreeSet<InternedString>)>,
380    // The used features set is the set of features which this local package had
381    // enabled, which is later used when compiling to instruct the code what
382    // features were enabled.
383    used: HashSet<InternedString>,
384    visited: HashSet<InternedString>,
385}
386
387impl Requirements<'_> {
388    fn new(summary: &Summary) -> Requirements<'_> {
389        Requirements {
390            summary,
391            deps: HashMap::new(),
392            used: HashSet::new(),
393            visited: HashSet::new(),
394        }
395    }
396
397    fn into_used(self) -> HashSet<InternedString> {
398        self.used
399    }
400
401    fn require_crate_feature(&mut self, package: InternedString, feat: InternedString) {
402        // If `package` is indeed an optional dependency then we activate the
403        // feature named `package`, but otherwise if `package` is a required
404        // dependency then there's no feature associated with it.
405        if let Some(dep) = self
406            .summary
407            .dependencies()
408            .iter()
409            .find(|p| p.name_in_toml() == package)
410        {
411            if dep.is_optional() {
412                self.used.insert(package);
413            }
414        }
415        self.deps
416            .entry(package)
417            .or_insert((false, BTreeSet::new()))
418            .1
419            .insert(feat);
420    }
421
422    fn seen(&mut self, feat: InternedString) -> bool {
423        if self.visited.insert(feat) {
424            self.used.insert(feat);
425            false
426        } else {
427            true
428        }
429    }
430
431    fn require_dependency(&mut self, pkg: InternedString) {
432        if self.seen(pkg) {
433            return;
434        }
435        self.deps.entry(pkg).or_insert((false, BTreeSet::new())).0 = true;
436    }
437
438    fn require_feature(&mut self, feat: InternedString) -> CargoResult<()> {
439        if feat.is_empty() || self.seen(feat) {
440            return Ok(());
441        }
442        for fv in self
443            .summary
444            .features()
445            .get(&feat)
446            .expect("must be a valid feature")
447        {
448            match *fv {
449                FeatureValue::Feature(ref dep_feat) if **dep_feat == *feat => anyhow::bail!(
450                    "cyclic feature dependency: feature `{}` depends on itself",
451                    feat
452                ),
453                _ => {}
454            }
455            self.require_value(fv)?;
456        }
457        Ok(())
458    }
459
460    fn require_value(&mut self, fv: &FeatureValue) -> CargoResult<()> {
461        match fv {
462            FeatureValue::Feature(feat) => self.require_feature(*feat)?,
463            FeatureValue::Crate(dep) => self.require_dependency(*dep),
464            FeatureValue::CrateFeature(dep, dep_feat) => {
465                self.require_crate_feature(*dep, *dep_feat)
466            }
467        };
468        Ok(())
469    }
470}