]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/registry.rs
Document lib before bin.
[cargo.git] / src / cargo / core / registry.rs
CommitLineData
44005146 1use std::collections::{HashMap, HashSet};
7192e29a 2
7f73a6c7 3use crate::core::PackageSet;
04ddd4d0
DW
4use crate::core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary};
5use crate::sources::config::SourceConfigMap;
ebca5190 6use crate::util::errors::CargoResult;
7f73a6c7 7use crate::util::interning::InternedString;
c6762ce4 8use crate::util::{profile, CanonicalUrl, Config};
ebca5190 9use anyhow::{bail, Context as _};
07162dba 10use log::{debug, trace};
07162dba 11use url::Url;
f4d6d021 12
bacb6be3 13/// Source of information about a group of packages.
c7641c24
PK
14///
15/// See also `core::Source`.
f4d6d021 16pub trait Registry {
c7641c24 17 /// Attempt to find the packages that match a dependency request.
76ce4dfe
AC
18 fn query(
19 &mut self,
20 dep: &Dependency,
21 f: &mut dyn FnMut(Summary),
22 fuzzy: bool,
23 ) -> CargoResult<()>;
842c182e 24
84cc3d8b 25 fn query_vec(&mut self, dep: &Dependency, fuzzy: bool) -> CargoResult<Vec<Summary>> {
842c182e 26 let mut ret = Vec::new();
84cc3d8b 27 self.query(dep, &mut |s| ret.push(s), fuzzy)?;
842c182e
AC
28 Ok(ret)
29 }
20cfb41e 30
e5a11190
E
31 fn describe_source(&self, source: SourceId) -> String;
32 fn is_replaced(&self, source: SourceId) -> bool;
8f6d4afb
CL
33}
34
816373d9
AC
35/// This structure represents a registry of known packages. It internally
36/// contains a number of `Box<Source>` instances which are used to load a
37/// `Package` from.
38///
39/// The resolution phase of Cargo uses this to drive knowledge about new
40/// packages as well as querying for lists of new packages. It is here that
f7c91ba6 41/// sources are updated (e.g., network operations) and overrides are
816373d9
AC
42/// handled.
43///
44/// The general idea behind this registry is that it is centered around the
bacb6be3 45/// `SourceMap` structure, contained within which is a mapping of a `SourceId` to
816373d9
AC
46/// a `Source`. Each `Source` in the map has been updated (using network
47/// operations if necessary) and is ready to be queried for packages.
2fe0bf83 48pub struct PackageRegistry<'cfg> {
c94804bd 49 config: &'cfg Config,
e3835f70 50 sources: SourceMap<'cfg>,
816373d9
AC
51
52 // A list of sources which are considered "overrides" which take precedent
53 // when querying for packages.
3b77b2c7 54 overrides: Vec<SourceId>,
6ab93117
AC
55
56 // Note that each SourceId does not take into account its `precise` field
57 // when hashing or testing for equality. When adding a new `SourceId`, we
58 // want to avoid duplicates in the `SourceMap` (to prevent re-updating the
59 // same git repo twice for example), but we also want to ensure that the
60 // loaded source is always updated.
61 //
62 // Sources with a `precise` field normally don't need to be updated because
63 // their contents are already on disk, but sources without a `precise` field
64 // almost always need to be updated. If we have a cached `Source` for a
65 // precise `SourceId`, then when we add a new `SourceId` that is not precise
66 // we want to ensure that the underlying source is updated.
67 //
68 // This is basically a long-winded way of saying that we want to know
69 // precisely what the keys of `sources` are, so this is a mapping of key to
70 // what exactly the key is.
71 source_ids: HashMap<SourceId, (SourceId, Kind)>,
72
e3835f70 73 locked: LockedMap,
44005146 74 yanked_whitelist: HashSet<PackageId>,
8214bb95 75 source_config: SourceConfigMap<'cfg>,
eebddd37 76
e5454122 77 patches: HashMap<CanonicalUrl, Vec<Summary>>,
eebddd37 78 patches_locked: bool,
e5454122 79 patches_available: HashMap<CanonicalUrl, Vec<PackageId>>,
9b278af1
YKCL
80}
81
803bf329
AC
82/// A map of all "locked packages" which is filled in when parsing a lock file
83/// and is used to guide dependency resolution by altering summaries as they're
84/// queried from this source.
85///
86/// This map can be thought of as a glorified `Vec<MySummary>` where `MySummary`
87/// has a `PackageId` for which package it represents as well as a list of
88/// `PackageId` for the resolved dependencies. The hash map is otherwise
89/// structured though for easy access throughout this registry.
90type LockedMap = HashMap<
91 // The first level of key-ing done in this hash map is the source that
92 // dependencies come from, identified by a `SourceId`.
8418a3ae
E
93 // The next level is keyed by the name of the package...
94 (SourceId, InternedString),
95 // ... and the value here is a list of tuples. The first element of each
96 // tuple is a package which has the source/name used to get to this
97 // point. The second element of each tuple is the list of locked
98 // dependencies that the first element has.
99 Vec<(PackageId, Vec<PackageId>)>,
803bf329 100>;
e3835f70 101
abe56727 102#[derive(PartialEq, Eq, Clone, Copy)]
6ab93117
AC
103enum Kind {
104 Override,
105 Locked,
106 Normal,
107}
108
fe484973
AC
109/// Argument to `PackageRegistry::patch` which is information about a `[patch]`
110/// directive that we found in a lockfile, if present.
111pub struct LockedPatchDependency {
112 /// The original `Dependency` directive, except "locked" so it's version
113 /// requirement is `=foo` and its `SourceId` has a "precise" listed.
114 pub dependency: Dependency,
115 /// The `PackageId` that was previously found in a lock file which
116 /// `dependency` matches.
117 pub package_id: PackageId,
118 /// Something only used for backwards compatibility with the v2 lock file
119 /// format where `branch=master` is considered the same as `DefaultBranch`.
120 /// For more comments on this see the code in `ops/resolve.rs`.
121 pub alt_package_id: Option<PackageId>,
122}
123
2fe0bf83 124impl<'cfg> PackageRegistry<'cfg> {
8214bb95 125 pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> {
82655b46 126 let source_config = SourceConfigMap::new(config)?;
8214bb95 127 Ok(PackageRegistry {
c94804bd 128 config,
e3835f70 129 sources: SourceMap::new(),
6ab93117 130 source_ids: HashMap::new(),
54d738b0 131 overrides: Vec::new(),
0247dc42 132 source_config,
816373d9 133 locked: HashMap::new(),
44005146 134 yanked_whitelist: HashSet::new(),
61a3c68b 135 patches: HashMap::new(),
eebddd37
AC
136 patches_locked: false,
137 patches_available: HashMap::new(),
8214bb95 138 })
98322afd
YKCL
139 }
140
468f243e 141 pub fn get(self, package_ids: &[PackageId]) -> CargoResult<PackageSet<'cfg>> {
e3835f70 142 trace!("getting packages; sources={}", self.sources.len());
c94804bd 143 PackageSet::new(package_ids, self.sources, self.config)
5451f95d
CL
144 }
145
e5a11190
E
146 fn ensure_loaded(&mut self, namespace: SourceId, kind: Kind) -> CargoResult<()> {
147 match self.source_ids.get(&namespace) {
6ab93117
AC
148 // We've previously loaded this source, and we've already locked it,
149 // so we're not allowed to change it even if `namespace` has a
150 // slightly different precise version listed.
07162dba 151 Some((_, Kind::Locked)) => {
f162aed2 152 debug!("load/locked {}", namespace);
1e682848 153 return Ok(());
f162aed2 154 }
6ab93117
AC
155
156 // If the previous source was not a precise source, then we can be
157 // sure that it's already been updated if we've already loaded it.
07162dba 158 Some((previous, _)) if previous.precise().is_none() => {
f162aed2 159 debug!("load/precise {}", namespace);
1e682848 160 return Ok(());
6ab93117
AC
161 }
162
163 // If the previous source has the same precise version as we do,
164 // then we're done, otherwise we need to need to move forward
165 // updating this source.
07162dba 166 Some((previous, _)) => {
7a2facba 167 if previous.precise() == namespace.precise() {
f162aed2 168 debug!("load/match {}", namespace);
1e682848 169 return Ok(());
6ab93117 170 }
f162aed2
AC
171 debug!("load/mismatch {}", namespace);
172 }
173 None => {
174 debug!("load/missing {}", namespace);
6ab93117 175 }
6ab93117 176 }
5451f95d 177
82655b46 178 self.load(namespace, kind)?;
9b278af1
YKCL
179 Ok(())
180 }
181
e5a11190
E
182 pub fn add_sources(&mut self, ids: impl IntoIterator<Item = SourceId>) -> CargoResult<()> {
183 for id in ids {
82655b46 184 self.ensure_loaded(id, Kind::Locked)?;
1a35097a
AC
185 }
186 Ok(())
187 }
188
b8b7faee 189 pub fn add_preloaded(&mut self, source: Box<dyn Source + 'cfg>) {
b02023ee 190 self.add_source(source, Kind::Locked);
bc60f64b
AC
191 }
192
b8b7faee 193 fn add_source(&mut self, source: Box<dyn Source + 'cfg>, kind: Kind) {
e5a11190 194 let id = source.source_id();
e3835f70 195 self.sources.insert(source);
e5a11190 196 self.source_ids.insert(id, (id, kind));
bc60f64b
AC
197 }
198
b8b7faee 199 pub fn add_override(&mut self, source: Box<dyn Source + 'cfg>) {
e5a11190 200 self.overrides.push(source.source_id());
b02023ee 201 self.add_source(source, Kind::Override);
e465ace4
TCS
202 }
203
2f7eb42f 204 pub fn add_to_yanked_whitelist(&mut self, iter: impl Iterator<Item = PackageId>) {
4a2f810d
AC
205 let pkgs = iter.collect::<Vec<_>>();
206 for (_, source) in self.sources.sources_mut() {
207 source.add_to_yanked_whitelist(&pkgs);
208 }
209 self.yanked_whitelist.extend(pkgs);
2f7eb42f
E
210 }
211
61e59278
E
212 /// remove all residual state from previous lock files.
213 pub fn clear_lock(&mut self) {
214 trace!("clear_lock");
215 self.locked = HashMap::new();
216 }
217
816373d9 218 pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {
155dee54
AC
219 trace!("register_lock: {}", id);
220 for dep in deps.iter() {
221 trace!("\t-> {}", dep);
222 }
8418a3ae 223 let sub_vec = self
e5a11190 224 .locked
8418a3ae
E
225 .entry((id.source_id(), id.name()))
226 .or_insert_with(Vec::new);
816373d9
AC
227 sub_vec.push((id, deps));
228 }
229
eebddd37
AC
230 /// Insert a `[patch]` section into this registry.
231 ///
232 /// This method will insert a `[patch]` section for the `url` specified,
233 /// with the given list of dependencies. The `url` specified is the URL of
234 /// the source to patch (for example this is `crates-io` in the manifest).
235 /// The `deps` is an array of all the entries in the `[patch]` section of
236 /// the manifest.
237 ///
238 /// Here the `deps` will be resolved to a precise version and stored
b78cb373
EH
239 /// internally for future calls to `query` below. `deps` should be a tuple
240 /// where the first element is the patch definition straight from the
241 /// manifest, and the second element is an optional variant where the
242 /// patch has been locked. This locked patch is the patch locked to
243 /// a specific version found in Cargo.lock. This will be `None` if
244 /// `Cargo.lock` doesn't exist, or the patch did not match any existing
245 /// entries in `Cargo.lock`.
eebddd37
AC
246 ///
247 /// Note that the patch list specified here *will not* be available to
248 /// `query` until `lock_patches` is called below, which should be called
249 /// once all patches have been added.
afa3aced
EH
250 ///
251 /// The return value is a `Vec` of patches that should *not* be locked.
252 /// This happens when the patch is locked, but the patch has been updated
253 /// so the locked value is no longer correct.
b78cb373
EH
254 pub fn patch(
255 &mut self,
256 url: &Url,
fe484973 257 deps: &[(&Dependency, Option<LockedPatchDependency>)],
afa3aced
EH
258 ) -> CargoResult<Vec<(Dependency, PackageId)>> {
259 // NOTE: None of this code is aware of required features. If a patch
260 // is missing a required feature, you end up with an "unused patch"
261 // warning, which is very hard to understand. Ideally the warning
262 // would be tailored to indicate *why* it is unused.
e5454122
AC
263 let canonical = CanonicalUrl::new(url)?;
264
afa3aced
EH
265 // Return value of patches that shouldn't be locked.
266 let mut unlock_patches = Vec::new();
267
eebddd37
AC
268 // First up we need to actually resolve each `deps` specification to
269 // precisely one summary. We're not using the `query` method below as it
270 // internally uses maps we're building up as part of this method
271 // (`patches_available` and `patches). Instead we're going straight to
272 // the source to load information from it.
273 //
274 // Remember that each dependency listed in `[patch]` has to resolve to
275 // precisely one package, so that's why we're just creating a flat list
276 // of summaries which should be the same length as `deps` above.
e5a11190
E
277 let unlocked_summaries = deps
278 .iter()
afa3aced
EH
279 .map(|(orig_patch, locked)| {
280 // Remove double reference in orig_patch. Is there maybe a
281 // magic pattern that could avoid this?
282 let orig_patch = *orig_patch;
283 // Use the locked patch if it exists, otherwise use the original.
284 let dep = match locked {
fe484973 285 Some(lock) => &lock.dependency,
afa3aced
EH
286 None => orig_patch,
287 };
e5a11190 288 debug!(
b4cd6095 289 "registering a patch for `{}` with `{}`",
e5a11190
E
290 url,
291 dep.package_name()
292 );
1e682848 293
8d2103f2 294 if dep.features().len() != 0 || !dep.uses_default_features() {
295 self.source_config.config().shell().warn(format!(
296 "patch for `{}` uses the features mechanism. \
297 default-features and features will not take effect because the patch dependency does not support this mechanism",
298 dep.package_name()
299 ))?;
300 }
301
1e682848
AC
302 // Go straight to the source for resolving `dep`. Load it as we
303 // normally would and then ask it directly for the list of summaries
304 // corresponding to this `dep`.
305 self.ensure_loaded(dep.source_id(), Kind::Normal)
ebca5190 306 .with_context(|| {
9099b491 307 format!(
a07fec1b 308 "failed to load source for dependency `{}`",
5295cadd 309 dep.package_name()
1e682848
AC
310 )
311 })?;
312
b78cb373 313 let source = self
e5a11190 314 .sources
1e682848 315 .get_mut(dep.source_id())
b78cb373
EH
316 .expect("loaded source not present");
317 let summaries = source.query_vec(dep)?;
ebca5190
WL
318 let (summary, should_unlock) = summary_for_patch(
319 orig_patch, locked, summaries, source,
320 )
321 .with_context(|| {
322 format!(
323 "patch for `{}` in `{}` failed to resolve",
324 orig_patch.package_name(),
325 url,
326 )
327 })?;
afa3aced
EH
328 debug!(
329 "patch summary is {:?} should_unlock={:?}",
330 summary, should_unlock
331 );
332 if let Some(unlock_id) = should_unlock {
333 unlock_patches.push((orig_patch.clone(), unlock_id));
334 }
b78cb373 335
e5454122 336 if *summary.package_id().source_id().canonical_url() == canonical {
3a18c89a 337 anyhow::bail!(
1e682848
AC
338 "patch for `{}` in `{}` points to the same source, but \
339 patches must point to different sources",
5295cadd 340 dep.package_name(),
1e682848
AC
341 url
342 );
343 }
344 Ok(summary)
345 })
346 .collect::<CargoResult<Vec<_>>>()
9099b491 347 .with_context(|| format!("failed to resolve patches for `{}`", url))?;
61a3c68b 348
803bf329
AC
349 let mut name_and_version = HashSet::new();
350 for summary in unlocked_summaries.iter() {
351 let name = summary.package_id().name();
352 let version = summary.package_id().version();
353 if !name_and_version.insert((name, version)) {
354 bail!(
355 "cannot have two `[patch]` entries which both resolve \
356 to `{} v{}`",
357 name,
358 version
359 );
360 }
361 }
362
fe484973
AC
363 // Calculate a list of all patches available for this source which is
364 // then used later during calls to `lock` to rewrite summaries to point
365 // directly at these patched entries.
366 //
367 // Note that this is somewhat subtle where the list of `ids` for a
368 // canonical URL is extend with possibly two ids per summary. This is done
369 // to handle the transition from the v2->v3 lock file format where in
370 // v2 DefeaultBranch was either DefaultBranch or Branch("master") for
371 // git dependencies. In this case if `summary.package_id()` is
372 // Branch("master") then alt_package_id will be DefaultBranch. This
373 // signifies that there's a patch available for either of those
374 // dependency directives if we see them in the dependency graph.
375 //
376 // This is a bit complicated and hopefully an edge case we can remove
377 // in the future, but for now it hopefully doesn't cause too much
378 // harm...
379 let mut ids = Vec::new();
380 for (summary, (_, lock)) in unlocked_summaries.iter().zip(deps) {
381 ids.push(summary.package_id());
382 if let Some(lock) = lock {
383 ids.extend(lock.alt_package_id);
384 }
385 }
386 self.patches_available.insert(canonical.clone(), ids);
387
eebddd37
AC
388 // Note that we do not use `lock` here to lock summaries! That step
389 // happens later once `lock_patches` is invoked. In the meantime though
390 // we want to fill in the `patches_available` map (later used in the
391 // `lock` method) and otherwise store the unlocked summaries in
392 // `patches` to get locked in a future call to `lock_patches`.
e5454122 393 self.patches.insert(canonical, unlocked_summaries);
61a3c68b 394
afa3aced 395 Ok(unlock_patches)
61a3c68b
AC
396 }
397
eebddd37
AC
398 /// Lock all patch summaries added via `patch`, making them available to
399 /// resolution via `query`.
400 ///
401 /// This function will internally `lock` each summary added via `patch`
402 /// above now that the full set of `patch` packages are known. This'll allow
403 /// us to correctly resolve overridden dependencies between patches
404 /// hopefully!
405 pub fn lock_patches(&mut self) {
406 assert!(!self.patches_locked);
407 for summaries in self.patches.values_mut() {
408 for summary in summaries {
afa3aced 409 debug!("locking patch {:?}", summary);
eebddd37
AC
410 *summary = lock(&self.locked, &self.patches_available, summary.clone());
411 }
412 }
413 self.patches_locked = true;
414 }
415
91791c4f
WL
416 /// Gets all patches grouped by the source URLS they are going to patch.
417 ///
418 /// These patches are mainly collected from [`patch`](Self::patch).
419 /// They might not be the same as patches actually used during dependency resolving.
420 pub fn patches(&self) -> &HashMap<CanonicalUrl, Vec<Summary>> {
421 &self.patches
61a3c68b
AC
422 }
423
e5a11190 424 fn load(&mut self, source_id: SourceId, kind: Kind) -> CargoResult<()> {
f2aa5b4c 425 (|| {
1868998b 426 debug!("loading source {}", source_id);
192073bf 427 let source = self.source_config.load(source_id, &self.yanked_whitelist)?;
b02023ee 428 assert_eq!(source.source_id(), source_id);
8214bb95 429
38d14a59 430 if kind == Kind::Override {
e5a11190 431 self.overrides.push(source_id);
f137281b 432 }
b02023ee 433 self.add_source(source, kind);
e465ace4 434
18e59304
AC
435 // Ensure the source has fetched all necessary remote data.
436 let _p = profile::start(format!("updating: {}", source_id));
e3835f70 437 self.sources.get_mut(source_id).unwrap().update()
1e682848 438 })()
9099b491 439 .with_context(|| format!("Unable to update {}", source_id))?;
37cffbe0 440 Ok(())
9b278af1 441 }
46f90ba5 442
1e682848 443 fn query_overrides(&mut self, dep: &Dependency) -> CargoResult<Option<Summary>> {
e5a11190 444 for &s in self.overrides.iter() {
e3835f70 445 let src = self.sources.get_mut(s).unwrap();
c14bb6e0 446 let dep = Dependency::new_override(dep.package_name(), s);
842c182e 447 let mut results = src.query_vec(&dep)?;
23591fe5 448 if !results.is_empty() {
1e682848 449 return Ok(Some(results.remove(0)));
fc0e6426 450 }
3b77b2c7 451 }
fc0e6426 452 Ok(None)
46f90ba5 453 }
816373d9 454
c0306a8a 455 /// This function is used to transform a summary to another locked summary
f7c91ba6 456 /// if possible. This is where the concept of a lock file comes into play.
c0306a8a 457 ///
f7c91ba6
AR
458 /// If a summary points at a package ID which was previously locked, then we
459 /// override the summary's ID itself, as well as all dependencies, to be
c0306a8a
AC
460 /// rewritten to the locked versions. This will transform the summary's
461 /// source to a precise source (listed in the locked version) as well as
462 /// transforming all of the dependencies from range requirements on
463 /// imprecise sources to exact requirements on precise sources.
464 ///
f7c91ba6 465 /// If a summary does not point at a package ID which was previously locked,
c0306a8a
AC
466 /// or if any dependencies were added and don't have a previously listed
467 /// version, we still want to avoid updating as many dependencies as
468 /// possible to keep the graph stable. In this case we map all of the
469 /// summary's dependencies to be rewritten to a locked version wherever
470 /// possible. If we're unable to map a dependency though, we just pass it on
471 /// through.
472 pub fn lock(&self, summary: Summary) -> Summary {
eebddd37
AC
473 assert!(self.patches_locked);
474 lock(&self.locked, &self.patches_available, summary)
816373d9 475 }
fc0e6426 476
1e682848
AC
477 fn warn_bad_override(
478 &self,
479 override_summary: &Summary,
480 real_summary: &Summary,
481 ) -> CargoResult<()> {
772e1a17 482 let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>();
fc0e6426
AC
483
484 let boilerplate = "\
485This is currently allowed but is known to produce buggy behavior with spurious
486recompiles and changes to the crate graph. Path overrides unfortunately were
487never intended to support this feature, so for now this message is just a
488warning. In the future, however, this message will become a hard error.
489
490To change the dependency graph via an override it's recommended to use the
0a40a0ae 491`[patch]` feature of Cargo instead of the path override feature. This is
fc0e6426
AC
492documented online at the url below for more information.
493
22db875b 494https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html
fc0e6426
AC
495";
496
497 for dep in override_summary.dependencies() {
772e1a17 498 if let Some(i) = real_deps.iter().position(|d| dep == *d) {
fc0e6426 499 real_deps.remove(i);
1e682848 500 continue;
fc0e6426 501 }
1e682848 502 let msg = format!(
a4e96114 503 "path override for crate `{}` has altered the original list of\n\
1e682848
AC
504 dependencies; the dependency on `{}` was either added or\n\
505 modified to not match the previously resolved version\n\n\
506 {}",
507 override_summary.package_id().name(),
5295cadd 508 dep.package_name(),
1e682848
AC
509 boilerplate
510 );
82655b46 511 self.source_config.config().shell().warn(&msg)?;
1e682848 512 return Ok(());
fc0e6426
AC
513 }
514
5295cadd 515 if let Some(dep) = real_deps.get(0) {
1e682848 516 let msg = format!(
a4e96114
EH
517 "path override for crate `{}` has altered the original list of\n\
518 dependencies; the dependency on `{}` was removed\n\n\
519 {}",
1e682848 520 override_summary.package_id().name(),
5295cadd 521 dep.package_name(),
1e682848
AC
522 boilerplate
523 );
82655b46 524 self.source_config.config().shell().warn(&msg)?;
1e682848 525 return Ok(());
fc0e6426
AC
526 }
527
528 Ok(())
529 }
9b278af1
YKCL
530}
531
2fe0bf83 532impl<'cfg> Registry for PackageRegistry<'cfg> {
76ce4dfe
AC
533 fn query(
534 &mut self,
535 dep: &Dependency,
536 f: &mut dyn FnMut(Summary),
537 fuzzy: bool,
538 ) -> CargoResult<()> {
eebddd37 539 assert!(self.patches_locked);
e3835f70
AC
540 let (override_summary, n, to_warn) = {
541 // Look for an override and get ready to query the real source.
23591fe5 542 let override_summary = self.query_overrides(dep)?;
61a3c68b
AC
543
544 // Next up on our list of candidates is to check the `[patch]`
545 // section of the manifest. Here we look through all patches
546 // relevant to the source that `dep` points to, and then we match
547 // name/version. Note that we don't use `dep.matches(..)` because
548 // the patches, by definition, come from a different source.
549 // This means that `dep.matches(..)` will always return false, when
550 // what we really care about is the name/version match.
551 let mut patches = Vec::<Summary>::new();
e5454122 552 if let Some(extra) = self.patches.get(dep.source_id().canonical_url()) {
1e682848
AC
553 patches.extend(
554 extra
555 .iter()
51d23560 556 .filter(|s| dep.matches_ignoring_source(s.package_id()))
1e682848
AC
557 .cloned(),
558 );
61a3c68b
AC
559 }
560
561 // A crucial feature of the `[patch]` feature is that we *don't*
562 // query the actual registry if we have a "locked" dependency. A
563 // locked dep basically just means a version constraint of `=a.b.c`,
564 // and because patches take priority over the actual source then if
565 // we have a candidate we're done.
566 if patches.len() == 1 && dep.is_locked() {
567 let patch = patches.remove(0);
568 match override_summary {
569 Some(summary) => (summary, 1, Some(patch)),
570 None => {
571 f(patch);
1e682848 572 return Ok(());
61a3c68b
AC
573 }
574 }
575 } else {
23591fe5 576 if !patches.is_empty() {
1e682848
AC
577 debug!(
578 "found {} patches with an unlocked dep on `{}` at {} \
579 with `{}`, \
580 looking at sources",
581 patches.len(),
5295cadd 582 dep.package_name(),
1e682848
AC
583 dep.source_id(),
584 dep.version_req()
585 );
e3835f70
AC
586 }
587
61a3c68b 588 // Ensure the requested source_id is loaded
1e682848 589 self.ensure_loaded(dep.source_id(), Kind::Normal)
ebca5190 590 .with_context(|| {
9099b491 591 format!(
a07fec1b 592 "failed to load source for dependency `{}`",
5295cadd 593 dep.package_name()
1e682848
AC
594 )
595 })?;
61a3c68b
AC
596
597 let source = self.sources.get_mut(dep.source_id());
598 match (override_summary, source) {
3a18c89a 599 (Some(_), None) => anyhow::bail!("override found but no real ones"),
61a3c68b
AC
600 (None, None) => return Ok(()),
601
602 // If we don't have an override then we just ship
603 // everything upstairs after locking the summary
604 (None, Some(source)) => {
605 for patch in patches.iter() {
606 f(patch.clone());
607 }
608
609 // Our sources shouldn't ever come back to us with two
610 // summaries that have the same version. We could,
611 // however, have an `[patch]` section which is in use
612 // to override a version in the registry. This means
613 // that if our `summary` in this loop has the same
614 // version as something in `patches` that we've
615 // already selected, then we skip this `summary`.
616 let locked = &self.locked;
eebddd37 617 let all_patches = &self.patches_available;
84cc3d8b 618 let callback = &mut |summary: Summary| {
61a3c68b
AC
619 for patch in patches.iter() {
620 let patch = patch.package_id().version();
621 if summary.package_id().version() == patch {
1e682848 622 return;
61a3c68b
AC
623 }
624 }
625 f(lock(locked, all_patches, summary))
84cc3d8b
E
626 };
627 return if fuzzy {
628 source.fuzzy_query(dep, callback)
629 } else {
630 source.query(dep, callback)
631 };
61a3c68b
AC
632 }
633
634 // If we have an override summary then we query the source
635 // to sanity check its results. We don't actually use any of
636 // the summaries it gives us though.
637 (Some(override_summary), Some(source)) => {
23591fe5 638 if !patches.is_empty() {
3a18c89a 639 anyhow::bail!("found patches and a path override")
61a3c68b
AC
640 }
641 let mut n = 0;
642 let mut to_warn = None;
84cc3d8b
E
643 {
644 let callback = &mut |summary| {
645 n += 1;
646 to_warn = Some(summary);
647 };
648 if fuzzy {
649 source.fuzzy_query(dep, callback)?;
650 } else {
651 source.query(dep, callback)?;
652 }
653 }
61a3c68b
AC
654 (override_summary, n, to_warn)
655 }
e3835f70 656 }
86774bd8 657 }
816373d9
AC
658 };
659
842c182e 660 if n > 1 {
3a18c89a 661 anyhow::bail!("found an override with a non-locked list");
842c182e
AC
662 } else if let Some(summary) = to_warn {
663 self.warn_bad_override(&override_summary, &summary)?;
664 }
665 f(self.lock(override_summary));
666 Ok(())
8f6d4afb 667 }
20cfb41e 668
e5a11190 669 fn describe_source(&self, id: SourceId) -> String {
20cfb41e
AC
670 match self.sources.get(id) {
671 Some(src) => src.describe(),
672 None => id.to_string(),
673 }
674 }
675
e5a11190 676 fn is_replaced(&self, id: SourceId) -> bool {
20cfb41e
AC
677 match self.sources.get(id) {
678 Some(src) => src.is_replaced(),
679 None => false,
680 }
681 }
f4d6d021 682}
e465ace4 683
e5454122
AC
684fn lock(
685 locked: &LockedMap,
686 patches: &HashMap<CanonicalUrl, Vec<PackageId>>,
687 summary: Summary,
688) -> Summary {
1e682848 689 let pair = locked
8418a3ae 690 .get(&(summary.source_id(), summary.name()))
dae87a26 691 .and_then(|vec| vec.iter().find(|&&(id, _)| id == summary.package_id()));
e3835f70
AC
692
693 trace!("locking summary of {}", summary.package_id());
694
f7c91ba6 695 // Lock the summary's ID if possible
e3835f70 696 let summary = match pair {
6694fdb6 697 Some((precise, _)) => summary.override_id(*precise),
e3835f70
AC
698 None => summary,
699 };
61a3c68b 700 summary.map_dependencies(|dep| {
e5a11190
E
701 trace!(
702 "\t{}/{}/{}",
703 dep.package_name(),
704 dep.version_req(),
705 dep.source_id()
706 );
e3835f70
AC
707
708 // If we've got a known set of overrides for this summary, then
709 // one of a few cases can arise:
710 //
711 // 1. We have a lock entry for this dependency from the same
712 // source as it's listed as coming from. In this case we make
f7c91ba6 713 // sure to lock to precisely the given package ID.
e3835f70
AC
714 //
715 // 2. We have a lock entry for this dependency, but it's from a
716 // different source than what's listed, or the version
717 // requirement has changed. In this case we must discard the
718 // locked version because the dependency needs to be
719 // re-resolved.
720 //
803bf329
AC
721 // 3. We have a lock entry for this dependency, but it's from a
722 // different source than what's listed. This lock though happens
723 // through `[patch]`, so we want to preserve it.
724 //
725 // 4. We don't have a lock entry for this dependency, in which
e3835f70
AC
726 // case it was likely an optional dependency which wasn't
727 // included previously so we just pass it through anyway.
728 //
803bf329
AC
729 // Cases 1/2 are handled by `matches_id`, case 3 is handled specially,
730 // and case 4 is handled by falling through to the logic below.
731 if let Some((_, locked_deps)) = pair {
732 let locked = locked_deps.iter().find(|&&id| {
733 // If the dependency matches the package id exactly then we've
734 // found a match, this is the id the dependency was previously
735 // locked to.
736 if dep.matches_id(id) {
737 return true;
738 }
739
740 // If the name/version doesn't match, then we definitely don't
741 // have a match whatsoever. Otherwise we need to check
742 // `[patch]`...
743 if !dep.matches_ignoring_source(id) {
744 return false;
745 }
746
747 // ... so here we look up the dependency url in the patches
748 // map, and we see if `id` is contained in the list of patches
749 // for that url. If it is then this lock is still valid,
750 // otherwise the lock is no longer valid.
e5454122 751 match patches.get(dep.source_id().canonical_url()) {
803bf329
AC
752 Some(list) => list.contains(&id),
753 None => false,
754 }
755 });
756
dae87a26 757 if let Some(&locked) = locked {
e3835f70 758 trace!("\tfirst hit on {}", locked);
35a2b86a 759 let mut dep = dep;
803bf329
AC
760
761 // If we found a locked version where the sources match, then
762 // we can `lock_to` to get an exact lock on this dependency.
763 // Otherwise we got a lock via `[patch]` so we only lock the
764 // version requirement, not the source.
765 if locked.source_id() == dep.source_id() {
766 dep.lock_to(locked);
767 } else {
c6762ce4 768 dep.lock_version(locked.version());
803bf329 769 }
1e682848 770 return dep;
e3835f70
AC
771 }
772 }
773
774 // If this dependency did not have a locked version, then we query
775 // all known locked packages to see if they match this dependency.
776 // If anything does then we lock it to that and move on.
1e682848 777 let v = locked
8418a3ae 778 .get(&(dep.source_id(), dep.package_name()))
dae87a26
E
779 .and_then(|vec| vec.iter().find(|&&(id, _)| dep.matches_id(id)));
780 if let Some(&(id, _)) = v {
61a3c68b 781 trace!("\tsecond hit on {}", id);
35a2b86a 782 let mut dep = dep;
61a3c68b 783 dep.lock_to(id);
1e682848 784 return dep;
61a3c68b
AC
785 }
786
61a3c68b 787 trace!("\tnope, unlocked");
23591fe5 788 dep
e3835f70
AC
789 })
790}
b78cb373 791
afa3aced 792/// This is a helper for selecting the summary, or generating a helpful error message.
b78cb373
EH
793fn summary_for_patch(
794 orig_patch: &Dependency,
fe484973 795 locked: &Option<LockedPatchDependency>,
b78cb373
EH
796 mut summaries: Vec<Summary>,
797 source: &mut dyn Source,
afa3aced 798) -> CargoResult<(Summary, Option<PackageId>)> {
b78cb373 799 if summaries.len() == 1 {
afa3aced 800 return Ok((summaries.pop().unwrap(), None));
b78cb373 801 }
b78cb373 802 if summaries.len() > 1 {
4f2bae93
EH
803 // TODO: In the future, it might be nice to add all of these
804 // candidates so that version selection would just pick the
805 // appropriate one. However, as this is currently structured, if we
806 // added these all as patches, the unselected versions would end up in
807 // the "unused patch" listing, and trigger a warning. It might take a
808 // fair bit of restructuring to make that work cleanly, and there
809 // isn't any demand at this time to support that.
810 let mut vers: Vec<_> = summaries.iter().map(|summary| summary.version()).collect();
811 vers.sort();
812 let versions: Vec<_> = vers.into_iter().map(|v| v.to_string()).collect();
813 anyhow::bail!(
814 "patch for `{}` in `{}` resolved to more than one candidate\n\
815 Found versions: {}\n\
816 Update the patch definition to select only one package.\n\
817 For example, add an `=` version requirement to the patch definition, \
818 such as `version = \"={}\"`.",
819 orig_patch.package_name(),
820 orig_patch.source_id(),
821 versions.join(", "),
822 versions.last().unwrap()
823 );
b78cb373 824 }
afa3aced 825 assert!(summaries.is_empty());
b78cb373 826 // No summaries found, try to help the user figure out what is wrong.
fe484973 827 if let Some(locked) = locked {
afa3aced 828 // Since the locked patch did not match anything, try the unlocked one.
6fd11a77 829 let orig_matches = source.query_vec(orig_patch).unwrap_or_else(|e| {
2c0cd974
EH
830 log::warn!(
831 "could not determine unlocked summaries for dep {:?}: {:?}",
832 orig_patch,
833 e
834 );
835 Vec::new()
836 });
6fd11a77 837 let (summary, _) = summary_for_patch(orig_patch, &None, orig_matches, source)?;
afa3aced
EH
838 // The unlocked version found a match. This returns a value to
839 // indicate that this entry should be unlocked.
fe484973 840 return Ok((summary, Some(locked.package_id)));
6fd11a77
EH
841 }
842 // Try checking if there are *any* packages that match this by name.
843 let name_only_dep = Dependency::new_override(orig_patch.package_name(), orig_patch.source_id());
844 let name_summaries = source.query_vec(&name_only_dep).unwrap_or_else(|e| {
845 log::warn!(
846 "failed to do name-only summary query for {:?}: {:?}",
847 name_only_dep,
848 e
849 );
850 Vec::new()
851 });
852 let mut vers = name_summaries
853 .iter()
854 .map(|summary| summary.version())
855 .collect::<Vec<_>>();
856 let found = match vers.len() {
857 0 => format!(""),
858 1 => format!("version `{}`", vers[0]),
859 _ => {
860 vers.sort();
861 let strs: Vec<_> = vers.into_iter().map(|v| v.to_string()).collect();
862 format!("versions `{}`", strs.join(", "))
b78cb373 863 }
6fd11a77
EH
864 };
865 if found.is_empty() {
866 anyhow::bail!(
5dde9cc9 867 "The patch location `{}` does not appear to contain any packages \
6fd11a77 868 matching the name `{}`.",
5dde9cc9 869 orig_patch.source_id(),
6fd11a77
EH
870 orig_patch.package_name()
871 );
872 } else {
873 anyhow::bail!(
5dde9cc9 874 "The patch location `{}` contains a `{}` package with {}, but the patch \
6fd11a77
EH
875 definition requires `{}`.\n\
876 Check that the version in the patch location is what you expect, \
877 and update the patch definition to match.",
5dde9cc9 878 orig_patch.source_id(),
6fd11a77
EH
879 orig_patch.package_name(),
880 found,
881 orig_patch.version_req()
882 );
afa3aced 883 }
b78cb373 884}