]>
Commit | Line | Data |
---|---|---|
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 | 12 | use std::cmp::Ordering; |
a372cae1 | 13 | use std::collections::{BTreeSet, HashMap, HashSet}; |
95de3a1d E |
14 | use std::rc::Rc; |
15 | ||
16 | use log::debug; | |
17 | ||
1eca786d EH |
18 | use crate::core::resolver::context::Context; |
19 | use crate::core::resolver::errors::describe_path; | |
949f49a5 | 20 | use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary}; |
a07fec1b | 21 | use crate::util::errors::{CargoResult, CargoResultExt}; |
7f73a6c7 | 22 | use crate::util::interning::InternedString; |
95de3a1d | 23 | |
c1b22c5b | 24 | use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet}; |
e26ef017 | 25 | use crate::core::resolver::{ActivateResult, ResolveOpts}; |
95de3a1d E |
26 | |
27 | pub 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 | ||
46 | impl<'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 |
256 | pub 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. | |
346 | fn 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 | ||
372 | struct 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 | ||
387 | impl 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 | } |