]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/resolver/dep_cache.rs
Fix resolve error with cyclic dev-dependency features.
[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
1eca786d 12use crate::core::resolver::context::Context;
7d22a309 13use crate::core::resolver::errors::describe_path_in_context;
07162dba 14use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
85854b18 15use crate::core::resolver::{
76a079cb
DM
16 ActivateError, ActivateResult, CliFeatures, RequestedFeatures, ResolveOpts, VersionOrdering,
17 VersionPreferences,
85854b18 18};
949f49a5 19use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary};
ebca5190 20use crate::util::errors::CargoResult;
7f73a6c7 21use crate::util::interning::InternedString;
ebca5190
WL
22
23use anyhow::Context as _;
07162dba 24use log::debug;
07162dba
AC
25use std::collections::{BTreeSet, HashMap, HashSet};
26use std::rc::Rc;
95de3a1d
E
27
28pub struct RegistryQueryer<'a> {
29 pub registry: &'a mut (dyn Registry + 'a),
30 replacements: &'a [(PackageIdSpec, Dependency)],
76a079cb 31 version_prefs: &'a VersionPreferences,
79714ba9
E
32 /// If set the list of dependency candidates will be sorted by minimal
33 /// versions first. That allows `cargo update -Z minimal-versions` which will
34 /// specify minimum dependency versions to be used.
95de3a1d 35 minimal_versions: bool,
79714ba9 36 /// a cache of `Candidate`s that fulfil a `Dependency`
c1b22c5b 37 registry_cache: HashMap<Dependency, Rc<Vec<Summary>>>,
79714ba9
E
38 /// a cache of `Dependency`s that are required for a `Summary`
39 summary_cache: HashMap<
e26ef017 40 (Option<PackageId>, Summary, ResolveOpts),
79714ba9
E
41 Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>,
42 >,
43 /// all the cases we ended up using a supplied replacement
c86b8345 44 used_replacements: HashMap<PackageId, Summary>,
95de3a1d
E
45}
46
47impl<'a> RegistryQueryer<'a> {
48 pub fn new(
49 registry: &'a mut dyn Registry,
50 replacements: &'a [(PackageIdSpec, Dependency)],
76a079cb 51 version_prefs: &'a VersionPreferences,
95de3a1d
E
52 minimal_versions: bool,
53 ) -> Self {
54 RegistryQueryer {
55 registry,
56 replacements,
76a079cb 57 version_prefs,
95de3a1d 58 minimal_versions,
79714ba9
E
59 registry_cache: HashMap::new(),
60 summary_cache: HashMap::new(),
95de3a1d
E
61 used_replacements: HashMap::new(),
62 }
63 }
64
65 pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
c86b8345
E
66 self.used_replacements.get(&p).map(|r| (p, r.package_id()))
67 }
68
69 pub fn replacement_summary(&self, p: PackageId) -> Option<&Summary> {
70 self.used_replacements.get(&p)
95de3a1d
E
71 }
72
73 /// Queries the `registry` to return a list of candidates for `dep`.
74 ///
75 /// This method is the location where overrides are taken into account. If
76 /// any candidates are returned which match an override then the override is
77 /// applied by performing a second query for what the override should
78 /// return.
c1b22c5b 79 pub fn query(&mut self, dep: &Dependency) -> CargoResult<Rc<Vec<Summary>>> {
79714ba9 80 if let Some(out) = self.registry_cache.get(dep).cloned() {
95de3a1d
E
81 return Ok(out);
82 }
83
84 let mut ret = Vec::new();
85 self.registry.query(
86 dep,
87 &mut |s| {
c1b22c5b 88 ret.push(s);
95de3a1d
E
89 },
90 false,
91 )?;
c1b22c5b 92 for summary in ret.iter_mut() {
95de3a1d
E
93 let mut potential_matches = self
94 .replacements
95 .iter()
96 .filter(|&&(ref spec, _)| spec.matches(summary.package_id()));
97
98 let &(ref spec, ref dep) = match potential_matches.next() {
99 None => continue,
100 Some(replacement) => replacement,
101 };
102 debug!(
103 "found an override for {} {}",
104 dep.package_name(),
105 dep.version_req()
106 );
107
108 let mut summaries = self.registry.query_vec(dep, false)?.into_iter();
109 let s = summaries.next().ok_or_else(|| {
3a18c89a 110 anyhow::format_err!(
95de3a1d
E
111 "no matching package for override `{}` found\n\
112 location searched: {}\n\
113 version required: {}",
114 spec,
115 dep.source_id(),
116 dep.version_req()
117 )
118 })?;
119 let summaries = summaries.collect::<Vec<_>>();
120 if !summaries.is_empty() {
121 let bullets = summaries
122 .iter()
123 .map(|s| format!(" * {}", s.package_id()))
124 .collect::<Vec<_>>();
3a18c89a 125 anyhow::bail!(
95de3a1d
E
126 "the replacement specification `{}` matched \
127 multiple packages:\n * {}\n{}",
128 spec,
129 s.package_id(),
130 bullets.join("\n")
131 );
132 }
133
134 // The dependency should be hard-coded to have the same name and an
135 // exact version requirement, so both of these assertions should
136 // never fail.
137 assert_eq!(s.version(), summary.version());
138 assert_eq!(s.name(), summary.name());
139
140 let replace = if s.source_id() == summary.source_id() {
141 debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
142 None
143 } else {
144 Some(s)
145 };
146 let matched_spec = spec.clone();
147
148 // Make sure no duplicates
149 if let Some(&(ref spec, _)) = potential_matches.next() {
3a18c89a 150 anyhow::bail!(
95de3a1d
E
151 "overlapping replacement specifications found:\n\n \
152 * {}\n * {}\n\nboth specifications match: {}",
153 matched_spec,
154 spec,
155 summary.package_id()
156 );
157 }
158
159 for dep in summary.dependencies() {
160 debug!("\t{} => {}", dep.package_name(), dep.version_req());
161 }
c86b8345
E
162 if let Some(r) = replace {
163 self.used_replacements.insert(summary.package_id(), r);
95de3a1d 164 }
95de3a1d
E
165 }
166
bd4a353e 167 // When we attempt versions for a package we'll want to do so in a sorted fashion to pick
76a079cb
DM
168 // the "best candidates" first. VersionPreferences implements this notion.
169 self.version_prefs.sort_summaries(
170 &mut ret,
171 if self.minimal_versions {
172 VersionOrdering::MinimumVersionsFirst
173 } else {
174 VersionOrdering::MaximumVersionsFirst
175 },
176 );
95de3a1d
E
177
178 let out = Rc::new(ret);
179
79714ba9 180 self.registry_cache.insert(dep.clone(), out.clone());
95de3a1d
E
181
182 Ok(out)
183 }
5ae13e32 184
623863e5 185 /// Find out what dependencies will be added by activating `candidate`,
e26ef017 186 /// with features described in `opts`. Then look up in the `registry`
623863e5
E
187 /// the candidates that will fulfil each of these dependencies, as it is the
188 /// next obvious question.
5ae13e32
E
189 pub fn build_deps(
190 &mut self,
1eca786d 191 cx: &Context,
5ae13e32
E
192 parent: Option<PackageId>,
193 candidate: &Summary,
e26ef017 194 opts: &ResolveOpts,
5ae13e32 195 ) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
623863e5
E
196 // if we have calculated a result before, then we can just return it,
197 // as it is a "pure" query of its arguments.
5ae13e32 198 if let Some(out) = self
79714ba9 199 .summary_cache
e26ef017 200 .get(&(parent, candidate.clone(), opts.clone()))
5ae13e32
E
201 .cloned()
202 {
203 return Ok(out);
204 }
205 // First, figure out our set of dependencies based on the requested set
206 // of features. This also calculates what features we're going to enable
207 // for our own dependencies.
e26ef017 208 let (used_features, deps) = resolve_features(parent, candidate, opts)?;
5ae13e32
E
209
210 // Next, transform all dependencies into a list of possible candidates
211 // which can satisfy that dependency.
212 let mut deps = deps
213 .into_iter()
214 .map(|(dep, features)| {
ebca5190 215 let candidates = self.query(&dep).with_context(|| {
9099b491 216 format!(
1eca786d 217 "failed to get `{}` as a dependency of {}",
a07fec1b 218 dep.package_name(),
7d22a309 219 describe_path_in_context(cx, &candidate.package_id()),
a07fec1b
EH
220 )
221 })?;
623863e5 222 Ok((dep, candidates, features))
5ae13e32
E
223 })
224 .collect::<CargoResult<Vec<DepInfo>>>()?;
225
226 // Attempt to resolve dependencies with fewer candidates before trying
227 // dependencies with more candidates. This way if the dependency with
228 // only one candidate can't be resolved we don't have to do a bunch of
229 // work before we figure that out.
230 deps.sort_by_key(|&(_, ref a, _)| a.len());
231
232 let out = Rc::new((used_features, Rc::new(deps)));
233
623863e5 234 // If we succeed we add the result to the cache so we can use it again next time.
34f2d471 235 // We don't cache the failure cases as they don't impl Clone.
79714ba9 236 self.summary_cache
e26ef017 237 .insert((parent, candidate.clone(), opts.clone()), out.clone());
5ae13e32
E
238
239 Ok(out)
240 }
c36301c6
E
241}
242
623863e5
E
243/// Returns the features we ended up using and
244/// all dependencies and the features we want from each of them.
949f49a5
E
245pub fn resolve_features<'b>(
246 parent: Option<PackageId>,
247 s: &'b Summary,
e26ef017 248 opts: &'b ResolveOpts,
623863e5 249) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
949f49a5
E
250 // First, filter by dev-dependencies.
251 let deps = s.dependencies();
e26ef017 252 let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
949f49a5 253
bcfdf9fb 254 let reqs = build_requirements(parent, s, opts)?;
949f49a5 255 let mut ret = Vec::new();
bcfdf9fb
EH
256 let default_dep = BTreeSet::new();
257 let mut valid_dep_names = HashSet::new();
949f49a5
E
258
259 // Next, collect all actually enabled dependencies and their features.
260 for dep in deps {
261 // Skip optional dependencies, but not those enabled through a
262 // feature
263 if dep.is_optional() && !reqs.deps.contains_key(&dep.name_in_toml()) {
264 continue;
265 }
bcfdf9fb 266 valid_dep_names.insert(dep.name_in_toml());
949f49a5
E
267 // So we want this dependency. Move the features we want from
268 // `feature_deps` to `ret` and register ourselves as using this
269 // name.
bcfdf9fb
EH
270 let mut base = reqs
271 .deps
272 .get(&dep.name_in_toml())
273 .unwrap_or(&default_dep)
274 .clone();
949f49a5 275 base.extend(dep.features().iter());
623863e5 276 ret.push((dep.clone(), Rc::new(base)));
949f49a5
E
277 }
278
bcfdf9fb 279 // This is a special case for command-line `--features
f4ac82a0 280 // dep_name/feat_name` where `dep_name` does not exist. All other
bcfdf9fb
EH
281 // validation is done either in `build_requirements` or
282 // `build_feature_map`.
2eee644c
EH
283 if parent.is_none() {
284 for dep_name in reqs.deps.keys() {
285 if !valid_dep_names.contains(dep_name) {
286 let e = RequirementError::MissingDependency(*dep_name);
287 return Err(e.into_activate_error(parent, s));
288 }
bcfdf9fb 289 }
949f49a5
E
290 }
291
bcfdf9fb 292 Ok((reqs.into_features(), ret))
949f49a5
E
293}
294
e26ef017 295/// Takes requested features for a single package from the input `ResolveOpts` and
949f49a5
E
296/// recurses to find all requested features, dependencies and requested
297/// dependency features in a `Requirements` object, returning it to the resolver.
298fn build_requirements<'a, 'b: 'a>(
bcfdf9fb 299 parent: Option<PackageId>,
949f49a5 300 s: &'a Summary,
e26ef017 301 opts: &'b ResolveOpts,
bcfdf9fb 302) -> ActivateResult<Requirements<'a>> {
949f49a5
E
303 let mut reqs = Requirements::new(s);
304
85854b18
EH
305 let handle_default = |uses_default_features, reqs: &mut Requirements<'_>| {
306 if uses_default_features && s.features().contains_key("default") {
307 if let Err(e) = reqs.require_feature(InternedString::new("default")) {
bcfdf9fb
EH
308 return Err(e.into_activate_error(parent, s));
309 }
e26ef017 310 }
85854b18
EH
311 Ok(())
312 };
313
314 match &opts.features {
315 RequestedFeatures::CliFeatures(CliFeatures {
316 features,
317 all_features,
318 uses_default_features,
319 }) => {
320 if *all_features {
321 for key in s.features().keys() {
322 if let Err(e) = reqs.require_feature(*key) {
323 return Err(e.into_activate_error(parent, s));
324 }
325 }
326 } else {
327 for fv in features.iter() {
1b7fa21f 328 if let Err(e) = reqs.require_value(fv) {
85854b18
EH
329 return Err(e.into_activate_error(parent, s));
330 }
331 }
332 handle_default(*uses_default_features, &mut reqs)?;
bcfdf9fb 333 }
949f49a5 334 }
85854b18
EH
335 RequestedFeatures::DepFeatures {
336 features,
337 uses_default_features,
338 } => {
339 for feature in features.iter() {
340 if let Err(e) = reqs.require_feature(*feature) {
341 return Err(e.into_activate_error(parent, s));
342 }
343 }
344 handle_default(*uses_default_features, &mut reqs)?;
bcfdf9fb 345 }
949f49a5 346 }
e26ef017 347
949f49a5
E
348 Ok(reqs)
349}
350
bcfdf9fb
EH
351/// Set of feature and dependency requirements for a package.
352#[derive(Debug)]
949f49a5
E
353struct Requirements<'a> {
354 summary: &'a Summary,
bcfdf9fb
EH
355 /// The deps map is a mapping of dependency name to list of features enabled.
356 ///
357 /// The resolver will activate all of these dependencies, with the given
358 /// features enabled.
359 deps: HashMap<InternedString, BTreeSet<InternedString>>,
360 /// The set of features enabled on this package which is later used when
361 /// compiling to instruct the code what features were enabled.
362 features: HashSet<InternedString>,
363}
364
365/// An error for a requirement.
366///
367/// This will later be converted to an `ActivateError` depending on whether or
368/// not this is a dependency or a root package.
369enum RequirementError {
370 /// The package does not have the requested feature.
371 MissingFeature(InternedString),
372 /// The package does not have the requested dependency.
373 MissingDependency(InternedString),
374 /// A feature has a direct cycle to itself.
375 ///
376 /// Note that cycles through multiple features are allowed (but perhaps
377 /// they shouldn't be?).
378 Cycle(InternedString),
949f49a5
E
379}
380
381impl Requirements<'_> {
382 fn new(summary: &Summary) -> Requirements<'_> {
383 Requirements {
384 summary,
385 deps: HashMap::new(),
bcfdf9fb 386 features: HashSet::new(),
949f49a5
E
387 }
388 }
389
bcfdf9fb
EH
390 fn into_features(self) -> HashSet<InternedString> {
391 self.features
949f49a5
E
392 }
393
f4ac82a0 394 fn require_dep_feature(
bcfdf9fb
EH
395 &mut self,
396 package: InternedString,
397 feat: InternedString,
9034e488 398 weak: bool,
bcfdf9fb 399 ) -> Result<(), RequirementError> {
dbc2c2b5
AC
400 // If `package` is indeed an optional dependency then we activate the
401 // feature named `package`, but otherwise if `package` is a required
402 // dependency then there's no feature associated with it.
9034e488 403 if !weak
bcfdf9fb
EH
404 && self
405 .summary
406 .dependencies()
407 .iter()
408 .any(|dep| dep.name_in_toml() == package && dep.is_optional())
dbc2c2b5 409 {
bcfdf9fb 410 self.require_feature(package)?;
949f49a5 411 }
bcfdf9fb
EH
412 self.deps.entry(package).or_default().insert(feat);
413 Ok(())
949f49a5
E
414 }
415
416 fn require_dependency(&mut self, pkg: InternedString) {
bcfdf9fb 417 self.deps.entry(pkg).or_default();
949f49a5
E
418 }
419
bcfdf9fb
EH
420 fn require_feature(&mut self, feat: InternedString) -> Result<(), RequirementError> {
421 if !self.features.insert(feat) {
422 // Already seen this feature.
949f49a5
E
423 return Ok(());
424 }
bcfdf9fb
EH
425
426 let fvs = match self.summary.features().get(&feat) {
427 Some(fvs) => fvs,
428 None => return Err(RequirementError::MissingFeature(feat)),
429 };
430
431 for fv in fvs {
432 if let FeatureValue::Feature(dep_feat) = fv {
433 if *dep_feat == feat {
434 return Err(RequirementError::Cycle(feat));
435 }
949f49a5
E
436 }
437 self.require_value(fv)?;
438 }
439 Ok(())
440 }
441
bcfdf9fb 442 fn require_value(&mut self, fv: &FeatureValue) -> Result<(), RequirementError> {
949f49a5
E
443 match fv {
444 FeatureValue::Feature(feat) => self.require_feature(*feat)?,
f4ac82a0
EH
445 FeatureValue::Dep { dep_name } => self.require_dependency(*dep_name),
446 FeatureValue::DepFeature {
bcfdf9fb
EH
447 dep_name,
448 dep_feature,
9ffcf690
EH
449 // Weak features are always activated in the dependency
450 // resolver. They will be narrowed inside the new feature
451 // resolver.
9034e488
EH
452 weak,
453 } => self.require_dep_feature(*dep_name, *dep_feature, *weak)?,
949f49a5
E
454 };
455 Ok(())
456 }
457}
bcfdf9fb
EH
458
459impl RequirementError {
460 fn into_activate_error(self, parent: Option<PackageId>, summary: &Summary) -> ActivateError {
461 match self {
462 RequirementError::MissingFeature(feat) => {
463 let deps: Vec<_> = summary
464 .dependencies()
465 .iter()
466 .filter(|dep| dep.name_in_toml() == feat)
467 .collect();
468 if deps.is_empty() {
469 return match parent {
470 None => ActivateError::Fatal(anyhow::format_err!(
471 "Package `{}` does not have the feature `{}`",
472 summary.package_id(),
473 feat
474 )),
475 Some(p) => ActivateError::Conflict(
476 p,
477 ConflictReason::MissingFeatures(feat.to_string()),
478 ),
479 };
480 }
481 if deps.iter().any(|dep| dep.is_optional()) {
482 match parent {
483 None => ActivateError::Fatal(anyhow::format_err!(
484 "Package `{}` does not have feature `{}`. It has an optional dependency \
f4ac82a0 485 with that name, but that dependency uses the \"dep:\" \
bcfdf9fb
EH
486 syntax in the features table, so it does not have an implicit feature with that name.",
487 summary.package_id(),
488 feat
489 )),
490 Some(p) => ActivateError::Conflict(
491 p,
492 ConflictReason::NonImplicitDependencyAsFeature(feat),
493 ),
494 }
495 } else {
496 match parent {
497 None => ActivateError::Fatal(anyhow::format_err!(
498 "Package `{}` does not have feature `{}`. It has a required dependency \
499 with that name, but only optional dependencies can be used as features.",
500 summary.package_id(),
501 feat
502 )),
503 Some(p) => ActivateError::Conflict(
504 p,
505 ConflictReason::RequiredDependencyAsFeature(feat),
506 ),
507 }
508 }
509 }
510 RequirementError::MissingDependency(dep_name) => {
511 match parent {
512 None => ActivateError::Fatal(anyhow::format_err!(
513 "package `{}` does not have a dependency named `{}`",
514 summary.package_id(),
515 dep_name
516 )),
517 // This code path currently isn't used, since `foo/bar`
f4ac82a0 518 // and `dep:` syntax is not allowed in a dependency.
bcfdf9fb
EH
519 Some(p) => ActivateError::Conflict(
520 p,
521 ConflictReason::MissingFeatures(dep_name.to_string()),
522 ),
523 }
524 }
525 RequirementError::Cycle(feat) => ActivateError::Fatal(anyhow::format_err!(
526 "cyclic feature dependency: feature `{}` depends on itself",
527 feat
528 )),
529 }
530 }
531}