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