]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/resolver/dep_cache.rs
Move string interning to util
[cargo.git] / src / cargo / core / resolver / dep_cache.rs
CommitLineData
623863e5
E
1//! There are 2 sources of facts for the resolver:
2//!
79714ba9
E
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.
623863e5
E
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//!
79714ba9 10//! This module impl that cache in all the gory details
623863e5 11
95de3a1d 12use std::cmp::Ordering;
a372cae1 13use std::collections::{BTreeSet, HashMap, HashSet};
95de3a1d
E
14use std::rc::Rc;
15
16use log::debug;
17
1eca786d
EH
18use crate::core::resolver::context::Context;
19use crate::core::resolver::errors::describe_path;
949f49a5 20use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary};
a07fec1b 21use crate::util::errors::{CargoResult, CargoResultExt};
7f73a6c7 22use crate::util::interning::InternedString;
95de3a1d 23
c1b22c5b 24use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
e26ef017 25use crate::core::resolver::{ActivateResult, ResolveOpts};
95de3a1d
E
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>,
79714ba9
E
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.
95de3a1d 34 minimal_versions: bool,
79714ba9 35 /// a cache of `Candidate`s that fulfil a `Dependency`
c1b22c5b 36 registry_cache: HashMap<Dependency, Rc<Vec<Summary>>>,
79714ba9
E
37 /// a cache of `Dependency`s that are required for a `Summary`
38 summary_cache: HashMap<
e26ef017 39 (Option<PackageId>, Summary, ResolveOpts),
79714ba9
E
40 Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
41 >,
42 /// all the cases we ended up using a supplied replacement
c86b8345 43 used_replacements: HashMap<PackageId, Summary>,
95de3a1d
E
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,
79714ba9
E
58 registry_cache: HashMap::new(),
59 summary_cache: HashMap::new(),
95de3a1d
E
60 used_replacements: HashMap::new(),
61 }
62 }
63
64 pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
c86b8345
E
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)
95de3a1d
E
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.
c1b22c5b 78 pub fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Summary>>> {
79714ba9 79 if let Some(out) = self.registry_cache.get(dep).cloned() {
95de3a1d
E
80 return Ok(out);
81 }
82
83 let mut ret = Vec::new();
84 self.registry.query(
85 dep,
86 &mut |s| {
c1b22c5b 87 ret.push(s);
95de3a1d
E
88 },
89 false,
90 )?;
c1b22c5b 91 for summary in ret.iter_mut() {
95de3a1d
E
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(|| {
3a18c89a 109 anyhow::format_err!(
95de3a1d
E
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<_>>();
3a18c89a 124 anyhow::bail!(
95de3a1d
E
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() {
3a18c89a 149 anyhow::bail!(
95de3a1d
E
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 }
c86b8345
E
161 if let Some(r) = replace {
162 self.used_replacements.insert(summary.package_id(), r);
95de3a1d 163 }
95de3a1d
E
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| {
c1b22c5b
E
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());
95de3a1d
E
173 let previous_cmp = a_in_previous.cmp(&b_in_previous).reverse();
174 match previous_cmp {
175 Ordering::Equal => {
c1b22c5b 176 let cmp = a.version().cmp(b.version());
95de3a1d
E
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
79714ba9 191 self.registry_cache.insert(dep.clone(), out.clone());
95de3a1d
E
192
193 Ok(out)
194 }
5ae13e32 195
623863e5 196 /// Find out what dependencies will be added by activating `candidate`,
e26ef017 197 /// with features described in `opts`. Then look up in the `registry`
623863e5
E
198 /// the candidates that will fulfil each of these dependencies, as it is the
199 /// next obvious question.
5ae13e32
E
200 pub fn build_deps(
201 &mut self,
1eca786d 202 cx: &Context,
5ae13e32
E
203 parent: Option<PackageId>,
204 candidate: &Summary,
e26ef017 205 opts: &ResolveOpts,
5ae13e32 206 ) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
623863e5
E
207 // if we have calculated a result before, then we can just return it,
208 // as it is a "pure" query of its arguments.
5ae13e32 209 if let Some(out) = self
79714ba9 210 .summary_cache
e26ef017 211 .get(&(parent, candidate.clone(), opts.clone()))
5ae13e32
E
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.
e26ef017 219 let (used_features, deps) = resolve_features(parent, candidate, opts)?;
5ae13e32
E
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)| {
a07fec1b
EH
226 let candidates = self.query(&dep).chain_err(|| {
227 anyhow::format_err!(
1eca786d 228 "failed to get `{}` as a dependency of {}",
a07fec1b 229 dep.package_name(),
1eca786d 230 describe_path(&cx.parents.path_to_bottom(&candidate.package_id())),
a07fec1b
EH
231 )
232 })?;
623863e5 233 Ok((dep, candidates, features))
5ae13e32
E
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
623863e5 245 // If we succeed we add the result to the cache so we can use it again next time.
34f2d471 246 // We don't cache the failure cases as they don't impl Clone.
79714ba9 247 self.summary_cache
e26ef017 248 .insert((parent, candidate.clone(), opts.clone()), out.clone());
5ae13e32
E
249
250 Ok(out)
251 }
c36301c6
E
252}
253
623863e5
E
254/// Returns the features we ended up using and
255/// all dependencies and the features we want from each of them.
949f49a5
E
256pub fn resolve_features<'b>(
257 parent: Option<PackageId>,
258 s: &'b Summary,
e26ef017 259 opts: &'b ResolveOpts,
623863e5 260) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
949f49a5
E
261 // First, filter by dev-dependencies.
262 let deps = s.dependencies();
e26ef017 263 let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
949f49a5 264
e26ef017 265 let reqs = build_requirements(s, opts)?;
949f49a5
E
266 let mut ret = Vec::new();
267 let mut used_features = HashSet::new();
a372cae1 268 let default_dep = (false, BTreeSet::new());
949f49a5
E
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 {
3a18c89a 289 None => anyhow::format_err!(
949f49a5
E
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('/') {
3a18c89a 307 return Err(anyhow::format_err!(
949f49a5
E
308 "feature names may not contain slashes: `{}`",
309 feature
310 )
311 .into());
312 }
313 }
623863e5 314 ret.push((dep.clone(), Rc::new(base)));
949f49a5
E
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 {
3a18c89a 330 None => anyhow::format_err!(
949f49a5
E
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
e26ef017 343/// Takes requested features for a single package from the input `ResolveOpts` and
949f49a5
E
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,
e26ef017 348 opts: &'b ResolveOpts,
949f49a5
E
349) -> CargoResult<Requirements<'a>> {
350 let mut reqs = Requirements::new(s);
351
db80baad 352 if opts.features.all_features {
e26ef017
EH
353 for key in s.features().keys() {
354 reqs.require_feature(*key)?;
949f49a5 355 }
e26ef017
EH
356 for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
357 reqs.require_dependency(dep.name_in_toml());
358 }
359 } else {
db80baad 360 for &f in opts.features.features.iter() {
e26ef017 361 reqs.require_value(&FeatureValue::new(f, s))?;
949f49a5
E
362 }
363 }
e26ef017 364
db80baad 365 if opts.features.uses_default_features && s.features().contains_key("default") {
61c1b1ca 366 reqs.require_feature(InternedString::new("default"))?;
949f49a5 367 }
e26ef017 368
949f49a5
E
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).
a372cae1 379 deps: HashMap<InternedString, (bool, BTreeSet<InternedString>)>,
949f49a5
E
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) {
dbc2c2b5
AC
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.
ab73b2c3 405 if self
dbc2c2b5
AC
406 .summary
407 .dependencies()
408 .iter()
ab73b2c3 409 .any(|dep| dep.name_in_toml() == package && dep.is_optional())
dbc2c2b5 410 {
ab73b2c3 411 self.used.insert(package);
dbc2c2b5 412 }
949f49a5
E
413 self.deps
414 .entry(package)
a372cae1 415 .or_insert((false, BTreeSet::new()))
949f49a5 416 .1
a372cae1 417 .insert(feat);
949f49a5
E
418 }
419
420 fn seen(&mut self, feat: InternedString) -> bool {
421 if self.visited.insert(feat) {
422 self.used.insert(feat);
423 false
424 } else {
425 true
426 }
427 }
428
429 fn require_dependency(&mut self, pkg: InternedString) {
430 if self.seen(pkg) {
431 return;
432 }
a372cae1 433 self.deps.entry(pkg).or_insert((false, BTreeSet::new())).0 = true;
949f49a5
E
434 }
435
436 fn require_feature(&mut self, feat: InternedString) -> CargoResult<()> {
437 if feat.is_empty() || self.seen(feat) {
438 return Ok(());
439 }
440 for fv in self
441 .summary
442 .features()
c14bb6e0 443 .get(&feat)
949f49a5
E
444 .expect("must be a valid feature")
445 {
446 match *fv {
3a18c89a 447 FeatureValue::Feature(ref dep_feat) if **dep_feat == *feat => anyhow::bail!(
949f49a5
E
448 "cyclic feature dependency: feature `{}` depends on itself",
449 feat
450 ),
451 _ => {}
452 }
453 self.require_value(fv)?;
454 }
455 Ok(())
456 }
457
458 fn require_value(&mut self, fv: &FeatureValue) -> CargoResult<()> {
459 match fv {
460 FeatureValue::Feature(feat) => self.require_feature(*feat)?,
461 FeatureValue::Crate(dep) => self.require_dependency(*dep),
462 FeatureValue::CrateFeature(dep, dep_feat) => {
463 self.require_crate_feature(*dep, *dep_feat)
464 }
465 };
466 Ok(())
467 }
468}