]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/registry.rs
Auto merge of #7771 - moxian:needless-borrow, r=alexcrichton
[cargo.git] / src / cargo / core / registry.rs
CommitLineData
44005146 1use std::collections::{HashMap, HashSet};
7192e29a 2
803bf329 3use failure::bail;
9ed82b57 4use log::{debug, trace};
61a3c68b
AC
5use semver::VersionReq;
6use url::Url;
7
04ddd4d0
DW
8use crate::core::PackageSet;
9use crate::core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary};
10use crate::sources::config::SourceConfigMap;
11use crate::util::errors::{CargoResult, CargoResultExt};
e5454122 12use crate::util::{profile, CanonicalUrl, Config};
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`.
94 SourceId,
95 HashMap<
96 // This next level is keyed by the name of the package...
97 String,
98 // ... and the value here is a list of tuples. The first element of each
99 // tuple is a package which has the source/name used to get to this
100 // point. The second element of each tuple is the list of locked
101 // dependencies that the first element has.
102 Vec<(PackageId, Vec<PackageId>)>,
103 >,
104>;
e3835f70 105
abe56727 106#[derive(PartialEq, Eq, Clone, Copy)]
6ab93117
AC
107enum Kind {
108 Override,
109 Locked,
110 Normal,
111}
112
2fe0bf83 113impl<'cfg> PackageRegistry<'cfg> {
8214bb95 114 pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> {
82655b46 115 let source_config = SourceConfigMap::new(config)?;
8214bb95 116 Ok(PackageRegistry {
c94804bd 117 config,
e3835f70 118 sources: SourceMap::new(),
6ab93117 119 source_ids: HashMap::new(),
54d738b0 120 overrides: Vec::new(),
0247dc42 121 source_config,
816373d9 122 locked: HashMap::new(),
44005146 123 yanked_whitelist: HashSet::new(),
61a3c68b 124 patches: HashMap::new(),
eebddd37
AC
125 patches_locked: false,
126 patches_available: HashMap::new(),
8214bb95 127 })
98322afd
YKCL
128 }
129
468f243e 130 pub fn get(self, package_ids: &[PackageId]) -> CargoResult<PackageSet<'cfg>> {
e3835f70 131 trace!("getting packages; sources={}", self.sources.len());
c94804bd 132 PackageSet::new(package_ids, self.sources, self.config)
5451f95d
CL
133 }
134
e5a11190
E
135 fn ensure_loaded(&mut self, namespace: SourceId, kind: Kind) -> CargoResult<()> {
136 match self.source_ids.get(&namespace) {
6ab93117
AC
137 // We've previously loaded this source, and we've already locked it,
138 // so we're not allowed to change it even if `namespace` has a
139 // slightly different precise version listed.
f162aed2
AC
140 Some(&(_, Kind::Locked)) => {
141 debug!("load/locked {}", namespace);
1e682848 142 return Ok(());
f162aed2 143 }
6ab93117
AC
144
145 // If the previous source was not a precise source, then we can be
146 // sure that it's already been updated if we've already loaded it.
7a2facba 147 Some(&(ref previous, _)) if previous.precise().is_none() => {
f162aed2 148 debug!("load/precise {}", namespace);
1e682848 149 return Ok(());
6ab93117
AC
150 }
151
152 // If the previous source has the same precise version as we do,
153 // then we're done, otherwise we need to need to move forward
154 // updating this source.
155 Some(&(ref previous, _)) => {
7a2facba 156 if previous.precise() == namespace.precise() {
f162aed2 157 debug!("load/match {}", namespace);
1e682848 158 return Ok(());
6ab93117 159 }
f162aed2
AC
160 debug!("load/mismatch {}", namespace);
161 }
162 None => {
163 debug!("load/missing {}", namespace);
6ab93117 164 }
6ab93117 165 }
5451f95d 166
82655b46 167 self.load(namespace, kind)?;
9b278af1
YKCL
168 Ok(())
169 }
170
e5a11190
E
171 pub fn add_sources(&mut self, ids: impl IntoIterator<Item = SourceId>) -> CargoResult<()> {
172 for id in ids {
82655b46 173 self.ensure_loaded(id, Kind::Locked)?;
1a35097a
AC
174 }
175 Ok(())
176 }
177
b8b7faee 178 pub fn add_preloaded(&mut self, source: Box<dyn Source + 'cfg>) {
b02023ee 179 self.add_source(source, Kind::Locked);
bc60f64b
AC
180 }
181
b8b7faee 182 fn add_source(&mut self, source: Box<dyn Source + 'cfg>, kind: Kind) {
e5a11190 183 let id = source.source_id();
e3835f70 184 self.sources.insert(source);
e5a11190 185 self.source_ids.insert(id, (id, kind));
bc60f64b
AC
186 }
187
b8b7faee 188 pub fn add_override(&mut self, source: Box<dyn Source + 'cfg>) {
e5a11190 189 self.overrides.push(source.source_id());
b02023ee 190 self.add_source(source, Kind::Override);
e465ace4
TCS
191 }
192
2f7eb42f 193 pub fn add_to_yanked_whitelist(&mut self, iter: impl Iterator<Item = PackageId>) {
4a2f810d
AC
194 let pkgs = iter.collect::<Vec<_>>();
195 for (_, source) in self.sources.sources_mut() {
196 source.add_to_yanked_whitelist(&pkgs);
197 }
198 self.yanked_whitelist.extend(pkgs);
2f7eb42f
E
199 }
200
816373d9 201 pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {
155dee54
AC
202 trace!("register_lock: {}", id);
203 for dep in deps.iter() {
204 trace!("\t-> {}", dep);
205 }
e5a11190
E
206 let sub_map = self
207 .locked
208 .entry(id.source_id())
1e682848
AC
209 .or_insert_with(HashMap::new);
210 let sub_vec = sub_map
211 .entry(id.name().to_string())
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
225 /// internally for future calls to `query` below. It's expected that `deps`
f7c91ba6 226 /// have had `lock_to` call already, if applicable. (e.g., if a lock file was
eebddd37
AC
227 /// already present).
228 ///
229 /// Note that the patch list specified here *will not* be available to
230 /// `query` until `lock_patches` is called below, which should be called
231 /// once all patches have been added.
61a3c68b 232 pub fn patch(&mut self, url: &Url, deps: &[Dependency]) -> CargoResult<()> {
e5454122
AC
233 let canonical = CanonicalUrl::new(url)?;
234
eebddd37
AC
235 // First up we need to actually resolve each `deps` specification to
236 // precisely one summary. We're not using the `query` method below as it
237 // internally uses maps we're building up as part of this method
238 // (`patches_available` and `patches). Instead we're going straight to
239 // the source to load information from it.
240 //
241 // Remember that each dependency listed in `[patch]` has to resolve to
242 // precisely one package, so that's why we're just creating a flat list
243 // of summaries which should be the same length as `deps` above.
e5a11190
E
244 let unlocked_summaries = deps
245 .iter()
1e682848 246 .map(|dep| {
e5a11190 247 debug!(
b4cd6095 248 "registering a patch for `{}` with `{}`",
e5a11190
E
249 url,
250 dep.package_name()
251 );
1e682848
AC
252
253 // Go straight to the source for resolving `dep`. Load it as we
254 // normally would and then ask it directly for the list of summaries
255 // corresponding to this `dep`.
256 self.ensure_loaded(dep.source_id(), Kind::Normal)
257 .chain_err(|| {
54c42142 258 failure::format_err!(
1e682848
AC
259 "failed to load source for a dependency \
260 on `{}`",
5295cadd 261 dep.package_name()
1e682848
AC
262 )
263 })?;
264
e5a11190
E
265 let mut summaries = self
266 .sources
1e682848
AC
267 .get_mut(dep.source_id())
268 .expect("loaded source not present")
269 .query_vec(dep)?
270 .into_iter();
271
272 let summary = match summaries.next() {
273 Some(summary) => summary,
54c42142 274 None => failure::bail!(
1e682848
AC
275 "patch for `{}` in `{}` did not resolve to any crates. If this is \
276 unexpected, you may wish to consult: \
277 https://github.com/rust-lang/cargo/issues/4678",
5295cadd 278 dep.package_name(),
1e682848
AC
279 url
280 ),
281 };
282 if summaries.next().is_some() {
54c42142 283 failure::bail!(
1e682848 284 "patch for `{}` in `{}` resolved to more than one candidate",
5295cadd 285 dep.package_name(),
1e682848
AC
286 url
287 )
61a3c68b 288 }
e5454122 289 if *summary.package_id().source_id().canonical_url() == canonical {
54c42142 290 failure::bail!(
1e682848
AC
291 "patch for `{}` in `{}` points to the same source, but \
292 patches must point to different sources",
5295cadd 293 dep.package_name(),
1e682848
AC
294 url
295 );
296 }
297 Ok(summary)
298 })
299 .collect::<CargoResult<Vec<_>>>()
54c42142 300 .chain_err(|| failure::format_err!("failed to resolve patches for `{}`", url))?;
61a3c68b 301
803bf329
AC
302 let mut name_and_version = HashSet::new();
303 for summary in unlocked_summaries.iter() {
304 let name = summary.package_id().name();
305 let version = summary.package_id().version();
306 if !name_and_version.insert((name, version)) {
307 bail!(
308 "cannot have two `[patch]` entries which both resolve \
309 to `{} v{}`",
310 name,
311 version
312 );
313 }
314 }
315
eebddd37
AC
316 // Note that we do not use `lock` here to lock summaries! That step
317 // happens later once `lock_patches` is invoked. In the meantime though
318 // we want to fill in the `patches_available` map (later used in the
319 // `lock` method) and otherwise store the unlocked summaries in
320 // `patches` to get locked in a future call to `lock_patches`.
dae87a26 321 let ids = unlocked_summaries.iter().map(|s| s.package_id()).collect();
e5454122
AC
322 self.patches_available.insert(canonical.clone(), ids);
323 self.patches.insert(canonical, unlocked_summaries);
61a3c68b
AC
324
325 Ok(())
326 }
327
eebddd37
AC
328 /// Lock all patch summaries added via `patch`, making them available to
329 /// resolution via `query`.
330 ///
331 /// This function will internally `lock` each summary added via `patch`
332 /// above now that the full set of `patch` packages are known. This'll allow
333 /// us to correctly resolve overridden dependencies between patches
334 /// hopefully!
335 pub fn lock_patches(&mut self) {
336 assert!(!self.patches_locked);
337 for summaries in self.patches.values_mut() {
338 for summary in summaries {
339 *summary = lock(&self.locked, &self.patches_available, summary.clone());
340 }
341 }
342 self.patches_locked = true;
343 }
344
e5454122
AC
345 pub fn patches(&self) -> Vec<Summary> {
346 self.patches
347 .values()
348 .flat_map(|v| v.iter().cloned())
349 .collect()
61a3c68b
AC
350 }
351
e5a11190 352 fn load(&mut self, source_id: SourceId, kind: Kind) -> CargoResult<()> {
f2aa5b4c 353 (|| {
1868998b 354 debug!("loading source {}", source_id);
192073bf 355 let source = self.source_config.load(source_id, &self.yanked_whitelist)?;
b02023ee 356 assert_eq!(source.source_id(), source_id);
8214bb95 357
38d14a59 358 if kind == Kind::Override {
e5a11190 359 self.overrides.push(source_id);
f137281b 360 }
b02023ee 361 self.add_source(source, kind);
e465ace4 362
18e59304
AC
363 // Ensure the source has fetched all necessary remote data.
364 let _p = profile::start(format!("updating: {}", source_id));
e3835f70 365 self.sources.get_mut(source_id).unwrap().update()
1e682848 366 })()
54c42142 367 .chain_err(|| failure::format_err!("Unable to update {}", source_id))?;
37cffbe0 368 Ok(())
9b278af1 369 }
46f90ba5 370
1e682848 371 fn query_overrides(&mut self, dep: &Dependency) -> CargoResult<Option<Summary>> {
e5a11190 372 for &s in self.overrides.iter() {
e3835f70 373 let src = self.sources.get_mut(s).unwrap();
c14bb6e0 374 let dep = Dependency::new_override(dep.package_name(), s);
842c182e 375 let mut results = src.query_vec(&dep)?;
23591fe5 376 if !results.is_empty() {
1e682848 377 return Ok(Some(results.remove(0)));
fc0e6426 378 }
3b77b2c7 379 }
fc0e6426 380 Ok(None)
46f90ba5 381 }
816373d9 382
c0306a8a 383 /// This function is used to transform a summary to another locked summary
f7c91ba6 384 /// if possible. This is where the concept of a lock file comes into play.
c0306a8a 385 ///
f7c91ba6
AR
386 /// If a summary points at a package ID which was previously locked, then we
387 /// override the summary's ID itself, as well as all dependencies, to be
c0306a8a
AC
388 /// rewritten to the locked versions. This will transform the summary's
389 /// source to a precise source (listed in the locked version) as well as
390 /// transforming all of the dependencies from range requirements on
391 /// imprecise sources to exact requirements on precise sources.
392 ///
f7c91ba6 393 /// If a summary does not point at a package ID which was previously locked,
c0306a8a
AC
394 /// or if any dependencies were added and don't have a previously listed
395 /// version, we still want to avoid updating as many dependencies as
396 /// possible to keep the graph stable. In this case we map all of the
397 /// summary's dependencies to be rewritten to a locked version wherever
398 /// possible. If we're unable to map a dependency though, we just pass it on
399 /// through.
400 pub fn lock(&self, summary: Summary) -> Summary {
eebddd37
AC
401 assert!(self.patches_locked);
402 lock(&self.locked, &self.patches_available, summary)
816373d9 403 }
fc0e6426 404
1e682848
AC
405 fn warn_bad_override(
406 &self,
407 override_summary: &Summary,
408 real_summary: &Summary,
409 ) -> CargoResult<()> {
772e1a17 410 let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>();
fc0e6426
AC
411
412 let boilerplate = "\
413This is currently allowed but is known to produce buggy behavior with spurious
414recompiles and changes to the crate graph. Path overrides unfortunately were
415never intended to support this feature, so for now this message is just a
416warning. In the future, however, this message will become a hard error.
417
418To change the dependency graph via an override it's recommended to use the
419`[replace]` feature of Cargo instead of the path override feature. This is
420documented online at the url below for more information.
421
22db875b 422https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html
fc0e6426
AC
423";
424
425 for dep in override_summary.dependencies() {
772e1a17 426 if let Some(i) = real_deps.iter().position(|d| dep == *d) {
fc0e6426 427 real_deps.remove(i);
1e682848 428 continue;
fc0e6426 429 }
1e682848 430 let msg = format!(
a4e96114 431 "path override for crate `{}` has altered the original list of\n\
1e682848
AC
432 dependencies; the dependency on `{}` was either added or\n\
433 modified to not match the previously resolved version\n\n\
434 {}",
435 override_summary.package_id().name(),
5295cadd 436 dep.package_name(),
1e682848
AC
437 boilerplate
438 );
82655b46 439 self.source_config.config().shell().warn(&msg)?;
1e682848 440 return Ok(());
fc0e6426
AC
441 }
442
5295cadd 443 if let Some(dep) = real_deps.get(0) {
1e682848 444 let msg = format!(
a4e96114
EH
445 "path override for crate `{}` has altered the original list of\n\
446 dependencies; the dependency on `{}` was removed\n\n\
447 {}",
1e682848 448 override_summary.package_id().name(),
5295cadd 449 dep.package_name(),
1e682848
AC
450 boilerplate
451 );
82655b46 452 self.source_config.config().shell().warn(&msg)?;
1e682848 453 return Ok(());
fc0e6426
AC
454 }
455
456 Ok(())
457 }
9b278af1
YKCL
458}
459
2fe0bf83 460impl<'cfg> Registry for PackageRegistry<'cfg> {
76ce4dfe
AC
461 fn query(
462 &mut self,
463 dep: &Dependency,
464 f: &mut dyn FnMut(Summary),
465 fuzzy: bool,
466 ) -> CargoResult<()> {
eebddd37 467 assert!(self.patches_locked);
e3835f70
AC
468 let (override_summary, n, to_warn) = {
469 // Look for an override and get ready to query the real source.
23591fe5 470 let override_summary = self.query_overrides(dep)?;
61a3c68b
AC
471
472 // Next up on our list of candidates is to check the `[patch]`
473 // section of the manifest. Here we look through all patches
474 // relevant to the source that `dep` points to, and then we match
475 // name/version. Note that we don't use `dep.matches(..)` because
476 // the patches, by definition, come from a different source.
477 // This means that `dep.matches(..)` will always return false, when
478 // what we really care about is the name/version match.
479 let mut patches = Vec::<Summary>::new();
e5454122 480 if let Some(extra) = self.patches.get(dep.source_id().canonical_url()) {
1e682848
AC
481 patches.extend(
482 extra
483 .iter()
51d23560 484 .filter(|s| dep.matches_ignoring_source(s.package_id()))
1e682848
AC
485 .cloned(),
486 );
61a3c68b
AC
487 }
488
489 // A crucial feature of the `[patch]` feature is that we *don't*
490 // query the actual registry if we have a "locked" dependency. A
491 // locked dep basically just means a version constraint of `=a.b.c`,
492 // and because patches take priority over the actual source then if
493 // we have a candidate we're done.
494 if patches.len() == 1 && dep.is_locked() {
495 let patch = patches.remove(0);
496 match override_summary {
497 Some(summary) => (summary, 1, Some(patch)),
498 None => {
499 f(patch);
1e682848 500 return Ok(());
61a3c68b
AC
501 }
502 }
503 } else {
23591fe5 504 if !patches.is_empty() {
1e682848
AC
505 debug!(
506 "found {} patches with an unlocked dep on `{}` at {} \
507 with `{}`, \
508 looking at sources",
509 patches.len(),
5295cadd 510 dep.package_name(),
1e682848
AC
511 dep.source_id(),
512 dep.version_req()
513 );
e3835f70
AC
514 }
515
61a3c68b 516 // Ensure the requested source_id is loaded
1e682848
AC
517 self.ensure_loaded(dep.source_id(), Kind::Normal)
518 .chain_err(|| {
54c42142 519 failure::format_err!(
1e682848
AC
520 "failed to load source for a dependency \
521 on `{}`",
5295cadd 522 dep.package_name()
1e682848
AC
523 )
524 })?;
61a3c68b
AC
525
526 let source = self.sources.get_mut(dep.source_id());
527 match (override_summary, source) {
54c42142 528 (Some(_), None) => failure::bail!("override found but no real ones"),
61a3c68b
AC
529 (None, None) => return Ok(()),
530
531 // If we don't have an override then we just ship
532 // everything upstairs after locking the summary
533 (None, Some(source)) => {
534 for patch in patches.iter() {
535 f(patch.clone());
536 }
537
538 // Our sources shouldn't ever come back to us with two
539 // summaries that have the same version. We could,
540 // however, have an `[patch]` section which is in use
541 // to override a version in the registry. This means
542 // that if our `summary` in this loop has the same
543 // version as something in `patches` that we've
544 // already selected, then we skip this `summary`.
545 let locked = &self.locked;
eebddd37 546 let all_patches = &self.patches_available;
84cc3d8b 547 let callback = &mut |summary: Summary| {
61a3c68b
AC
548 for patch in patches.iter() {
549 let patch = patch.package_id().version();
550 if summary.package_id().version() == patch {
1e682848 551 return;
61a3c68b
AC
552 }
553 }
554 f(lock(locked, all_patches, summary))
84cc3d8b
E
555 };
556 return if fuzzy {
557 source.fuzzy_query(dep, callback)
558 } else {
559 source.query(dep, callback)
560 };
61a3c68b
AC
561 }
562
563 // If we have an override summary then we query the source
564 // to sanity check its results. We don't actually use any of
565 // the summaries it gives us though.
566 (Some(override_summary), Some(source)) => {
23591fe5 567 if !patches.is_empty() {
54c42142 568 failure::bail!("found patches and a path override")
61a3c68b
AC
569 }
570 let mut n = 0;
571 let mut to_warn = None;
84cc3d8b
E
572 {
573 let callback = &mut |summary| {
574 n += 1;
575 to_warn = Some(summary);
576 };
577 if fuzzy {
578 source.fuzzy_query(dep, callback)?;
579 } else {
580 source.query(dep, callback)?;
581 }
582 }
61a3c68b
AC
583 (override_summary, n, to_warn)
584 }
e3835f70 585 }
86774bd8 586 }
816373d9
AC
587 };
588
842c182e 589 if n > 1 {
54c42142 590 failure::bail!("found an override with a non-locked list");
842c182e
AC
591 } else if let Some(summary) = to_warn {
592 self.warn_bad_override(&override_summary, &summary)?;
593 }
594 f(self.lock(override_summary));
595 Ok(())
8f6d4afb 596 }
20cfb41e 597
e5a11190 598 fn describe_source(&self, id: SourceId) -> String {
20cfb41e
AC
599 match self.sources.get(id) {
600 Some(src) => src.describe(),
601 None => id.to_string(),
602 }
603 }
604
e5a11190 605 fn is_replaced(&self, id: SourceId) -> bool {
20cfb41e
AC
606 match self.sources.get(id) {
607 Some(src) => src.is_replaced(),
608 None => false,
609 }
610 }
f4d6d021 611}
e465ace4 612
e5454122
AC
613fn lock(
614 locked: &LockedMap,
615 patches: &HashMap<CanonicalUrl, Vec<PackageId>>,
616 summary: Summary,
617) -> Summary {
1e682848 618 let pair = locked
e5a11190 619 .get(&summary.source_id())
1e682848 620 .and_then(|map| map.get(&*summary.name()))
dae87a26 621 .and_then(|vec| vec.iter().find(|&&(id, _)| id == summary.package_id()));
e3835f70
AC
622
623 trace!("locking summary of {}", summary.package_id());
624
f7c91ba6 625 // Lock the summary's ID if possible
e3835f70 626 let summary = match pair {
803bf329 627 Some((precise, _)) => summary.override_id(precise.clone()),
e3835f70
AC
628 None => summary,
629 };
61a3c68b 630 summary.map_dependencies(|dep| {
e5a11190
E
631 trace!(
632 "\t{}/{}/{}",
633 dep.package_name(),
634 dep.version_req(),
635 dep.source_id()
636 );
e3835f70
AC
637
638 // If we've got a known set of overrides for this summary, then
639 // one of a few cases can arise:
640 //
641 // 1. We have a lock entry for this dependency from the same
642 // source as it's listed as coming from. In this case we make
f7c91ba6 643 // sure to lock to precisely the given package ID.
e3835f70
AC
644 //
645 // 2. We have a lock entry for this dependency, but it's from a
646 // different source than what's listed, or the version
647 // requirement has changed. In this case we must discard the
648 // locked version because the dependency needs to be
649 // re-resolved.
650 //
803bf329
AC
651 // 3. We have a lock entry for this dependency, but it's from a
652 // different source than what's listed. This lock though happens
653 // through `[patch]`, so we want to preserve it.
654 //
655 // 4. We don't have a lock entry for this dependency, in which
e3835f70
AC
656 // case it was likely an optional dependency which wasn't
657 // included previously so we just pass it through anyway.
658 //
803bf329
AC
659 // Cases 1/2 are handled by `matches_id`, case 3 is handled specially,
660 // and case 4 is handled by falling through to the logic below.
661 if let Some((_, locked_deps)) = pair {
662 let locked = locked_deps.iter().find(|&&id| {
663 // If the dependency matches the package id exactly then we've
664 // found a match, this is the id the dependency was previously
665 // locked to.
666 if dep.matches_id(id) {
667 return true;
668 }
669
670 // If the name/version doesn't match, then we definitely don't
671 // have a match whatsoever. Otherwise we need to check
672 // `[patch]`...
673 if !dep.matches_ignoring_source(id) {
674 return false;
675 }
676
677 // ... so here we look up the dependency url in the patches
678 // map, and we see if `id` is contained in the list of patches
679 // for that url. If it is then this lock is still valid,
680 // otherwise the lock is no longer valid.
e5454122 681 match patches.get(dep.source_id().canonical_url()) {
803bf329
AC
682 Some(list) => list.contains(&id),
683 None => false,
684 }
685 });
686
dae87a26 687 if let Some(&locked) = locked {
e3835f70 688 trace!("\tfirst hit on {}", locked);
35a2b86a 689 let mut dep = dep;
803bf329
AC
690
691 // If we found a locked version where the sources match, then
692 // we can `lock_to` to get an exact lock on this dependency.
693 // Otherwise we got a lock via `[patch]` so we only lock the
694 // version requirement, not the source.
695 if locked.source_id() == dep.source_id() {
696 dep.lock_to(locked);
697 } else {
698 let req = VersionReq::exact(locked.version());
699 dep.set_version_req(req);
700 }
1e682848 701 return dep;
e3835f70
AC
702 }
703 }
704
705 // If this dependency did not have a locked version, then we query
706 // all known locked packages to see if they match this dependency.
707 // If anything does then we lock it to that and move on.
1e682848 708 let v = locked
e5a11190 709 .get(&dep.source_id())
5295cadd 710 .and_then(|map| map.get(&*dep.package_name()))
dae87a26
E
711 .and_then(|vec| vec.iter().find(|&&(id, _)| dep.matches_id(id)));
712 if let Some(&(id, _)) = v {
61a3c68b 713 trace!("\tsecond hit on {}", id);
35a2b86a 714 let mut dep = dep;
61a3c68b 715 dep.lock_to(id);
1e682848 716 return dep;
61a3c68b
AC
717 }
718
61a3c68b 719 trace!("\tnope, unlocked");
23591fe5 720 dep
e3835f70
AC
721 })
722}