]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/registry.rs
More lint cleaning
[cargo.git] / src / cargo / core / registry.rs
CommitLineData
fc0e6426 1use std::collections::HashMap;
7192e29a 2
61a3c68b
AC
3use semver::VersionReq;
4use url::Url;
5
d7c8c189 6use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId};
2d25fcac 7use core::PackageSet;
c7de4859 8use util::{Config, profile};
e95044e3 9use util::errors::{CargoResult, CargoResultExt};
8214bb95 10use sources::config::SourceConfigMap;
f4d6d021 11
bacb6be3 12/// Source of information about a group of packages.
c7641c24
PK
13///
14/// See also `core::Source`.
f4d6d021 15pub trait Registry {
c7641c24 16 /// Attempt to find the packages that match a dependency request.
842c182e
AC
17 fn query(&mut self,
18 dep: &Dependency,
19 f: &mut FnMut(Summary)) -> CargoResult<()>;
20
21 fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
22 let mut ret = Vec::new();
23 self.query(dep, &mut |s| ret.push(s))?;
24 Ok(ret)
25 }
5430db61
AC
26
27 /// Returns whether or not this registry will return summaries with
28 /// checksums listed.
5b08b8fe
AC
29 fn supports_checksums(&self) -> bool;
30
31 /// Returns whether or not this registry will return summaries with
32 /// the `precise` field in the source id listed.
33 fn requires_precise(&self) -> bool;
8f6d4afb
CL
34}
35
7ff9cbeb 36impl<'a, T: ?Sized + Registry + 'a> Registry for Box<T> {
842c182e
AC
37 fn query(&mut self,
38 dep: &Dependency,
39 f: &mut FnMut(Summary)) -> CargoResult<()> {
40 (**self).query(dep, f)
7ff9cbeb 41 }
5b08b8fe
AC
42
43 fn supports_checksums(&self) -> bool {
44 (**self).supports_checksums()
45 }
46
47 fn requires_precise(&self) -> bool {
48 (**self).requires_precise()
49 }
7ff9cbeb
AC
50}
51
816373d9
AC
52/// This structure represents a registry of known packages. It internally
53/// contains a number of `Box<Source>` instances which are used to load a
54/// `Package` from.
55///
56/// The resolution phase of Cargo uses this to drive knowledge about new
57/// packages as well as querying for lists of new packages. It is here that
bacb6be3 58/// sources are updated (e.g. network operations) and overrides are
816373d9
AC
59/// handled.
60///
61/// The general idea behind this registry is that it is centered around the
bacb6be3 62/// `SourceMap` structure, contained within which is a mapping of a `SourceId` to
816373d9
AC
63/// a `Source`. Each `Source` in the map has been updated (using network
64/// operations if necessary) and is ready to be queried for packages.
2fe0bf83 65pub struct PackageRegistry<'cfg> {
e3835f70 66 sources: SourceMap<'cfg>,
816373d9
AC
67
68 // A list of sources which are considered "overrides" which take precedent
69 // when querying for packages.
3b77b2c7 70 overrides: Vec<SourceId>,
6ab93117
AC
71
72 // Note that each SourceId does not take into account its `precise` field
73 // when hashing or testing for equality. When adding a new `SourceId`, we
74 // want to avoid duplicates in the `SourceMap` (to prevent re-updating the
75 // same git repo twice for example), but we also want to ensure that the
76 // loaded source is always updated.
77 //
78 // Sources with a `precise` field normally don't need to be updated because
79 // their contents are already on disk, but sources without a `precise` field
80 // almost always need to be updated. If we have a cached `Source` for a
81 // precise `SourceId`, then when we add a new `SourceId` that is not precise
82 // we want to ensure that the underlying source is updated.
83 //
84 // This is basically a long-winded way of saying that we want to know
85 // precisely what the keys of `sources` are, so this is a mapping of key to
86 // what exactly the key is.
87 source_ids: HashMap<SourceId, (SourceId, Kind)>,
88
e3835f70 89 locked: LockedMap,
8214bb95 90 source_config: SourceConfigMap<'cfg>,
61a3c68b 91 patches: HashMap<Url, Vec<Summary>>,
9b278af1
YKCL
92}
93
e3835f70
AC
94type LockedMap = HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>;
95
abe56727 96#[derive(PartialEq, Eq, Clone, Copy)]
6ab93117
AC
97enum Kind {
98 Override,
99 Locked,
100 Normal,
101}
102
2fe0bf83 103impl<'cfg> PackageRegistry<'cfg> {
8214bb95 104 pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> {
82655b46 105 let source_config = SourceConfigMap::new(config)?;
8214bb95 106 Ok(PackageRegistry {
e3835f70 107 sources: SourceMap::new(),
6ab93117 108 source_ids: HashMap::new(),
54d738b0 109 overrides: Vec::new(),
8214bb95 110 source_config: source_config,
816373d9 111 locked: HashMap::new(),
61a3c68b 112 patches: HashMap::new(),
8214bb95 113 })
98322afd
YKCL
114 }
115
f9945926 116 pub fn get(self, package_ids: &[PackageId]) -> PackageSet<'cfg> {
e3835f70
AC
117 trace!("getting packages; sources={}", self.sources.len());
118 PackageSet::new(package_ids, self.sources)
5451f95d
CL
119 }
120
f0647ea2 121 fn ensure_loaded(&mut self, namespace: &SourceId, kind: Kind) -> CargoResult<()> {
edc6ebc5 122 match self.source_ids.get(namespace) {
6ab93117
AC
123 // We've previously loaded this source, and we've already locked it,
124 // so we're not allowed to change it even if `namespace` has a
125 // slightly different precise version listed.
f162aed2
AC
126 Some(&(_, Kind::Locked)) => {
127 debug!("load/locked {}", namespace);
128 return Ok(())
129 }
6ab93117
AC
130
131 // If the previous source was not a precise source, then we can be
132 // sure that it's already been updated if we've already loaded it.
7a2facba 133 Some(&(ref previous, _)) if previous.precise().is_none() => {
f162aed2 134 debug!("load/precise {}", namespace);
6ab93117
AC
135 return Ok(())
136 }
137
138 // If the previous source has the same precise version as we do,
139 // then we're done, otherwise we need to need to move forward
140 // updating this source.
141 Some(&(ref previous, _)) => {
7a2facba 142 if previous.precise() == namespace.precise() {
f162aed2 143 debug!("load/match {}", namespace);
6ab93117
AC
144 return Ok(())
145 }
f162aed2
AC
146 debug!("load/mismatch {}", namespace);
147 }
148 None => {
149 debug!("load/missing {}", namespace);
6ab93117 150 }
6ab93117 151 }
5451f95d 152
82655b46 153 self.load(namespace, kind)?;
9b278af1
YKCL
154 Ok(())
155 }
156
c94648ba
AC
157 pub fn add_sources(&mut self, ids: &[SourceId]) -> CargoResult<()> {
158 for id in ids.iter() {
82655b46 159 self.ensure_loaded(id, Kind::Locked)?;
1a35097a
AC
160 }
161 Ok(())
162 }
163
b02023ee
AK
164 pub fn add_preloaded(&mut self, source: Box<Source + 'cfg>) {
165 self.add_source(source, Kind::Locked);
bc60f64b
AC
166 }
167
68c6f2b0 168 fn add_source(&mut self, source: Box<Source + 'cfg>, kind: Kind) {
b02023ee 169 let id = source.source_id().clone();
e3835f70 170 self.sources.insert(source);
b02023ee 171 self.source_ids.insert(id.clone(), (id, kind));
bc60f64b
AC
172 }
173
b02023ee
AK
174 pub fn add_override(&mut self, source: Box<Source + 'cfg>) {
175 self.overrides.push(source.source_id().clone());
176 self.add_source(source, Kind::Override);
e465ace4
TCS
177 }
178
816373d9 179 pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {
155dee54
AC
180 trace!("register_lock: {}", id);
181 for dep in deps.iter() {
182 trace!("\t-> {}", dep);
183 }
6e6dec8a
AC
184 let sub_map = self.locked.entry(id.source_id().clone())
185 .or_insert(HashMap::new());
186 let sub_vec = sub_map.entry(id.name().to_string())
187 .or_insert(Vec::new());
816373d9
AC
188 sub_vec.push((id, deps));
189 }
190
61a3c68b
AC
191 pub fn patch(&mut self, url: &Url, deps: &[Dependency]) -> CargoResult<()> {
192 let deps = deps.iter().map(|dep| {
193 let mut summaries = self.query_vec(dep)?.into_iter();
194 let summary = match summaries.next() {
195 Some(summary) => summary,
196 None => {
197 bail!("patch for `{}` in `{}` did not resolve to any crates",
198 dep.name(), url)
199 }
200 };
201 if summaries.next().is_some() {
202 bail!("patch for `{}` in `{}` resolved to more than one candidate",
203 dep.name(), url)
204 }
205 if summary.package_id().source_id().url() == url {
206 bail!("patch for `{}` in `{}` points to the same source, but \
207 patches must point to different sources",
208 dep.name(), url);
209 }
210 Ok(summary)
211 }).collect::<CargoResult<Vec<_>>>().chain_err(|| {
212 format!("failed to resolve patches for `{}`", url)
213 })?;
214
215 self.patches.insert(url.clone(), deps);
216
217 Ok(())
218 }
219
220 pub fn patches(&self) -> &HashMap<Url, Vec<Summary>> {
221 &self.patches
222 }
223
6ab93117 224 fn load(&mut self, source_id: &SourceId, kind: Kind) -> CargoResult<()> {
f2aa5b4c 225 (|| {
82655b46 226 let source = self.source_config.load(source_id)?;
b02023ee 227 assert_eq!(source.source_id(), source_id);
8214bb95 228
38d14a59 229 if kind == Kind::Override {
3b77b2c7 230 self.overrides.push(source_id.clone());
f137281b 231 }
b02023ee 232 self.add_source(source, kind);
e465ace4 233
18e59304
AC
234 // Ensure the source has fetched all necessary remote data.
235 let _p = profile::start(format!("updating: {}", source_id));
e3835f70 236 self.sources.get_mut(source_id).unwrap().update()
c7de4859 237 })().chain_err(|| format!("Unable to update {}", source_id))
9b278af1 238 }
46f90ba5 239
3b77b2c7 240 fn query_overrides(&mut self, dep: &Dependency)
fc0e6426 241 -> CargoResult<Option<Summary>> {
3b77b2c7 242 for s in self.overrides.iter() {
e3835f70 243 let src = self.sources.get_mut(s).unwrap();
7a2facba 244 let dep = Dependency::new_override(dep.name(), s);
842c182e 245 let mut results = src.query_vec(&dep)?;
23591fe5 246 if !results.is_empty() {
fc0e6426
AC
247 return Ok(Some(results.remove(0)))
248 }
3b77b2c7 249 }
fc0e6426 250 Ok(None)
46f90ba5 251 }
816373d9 252
c0306a8a
AC
253 /// This function is used to transform a summary to another locked summary
254 /// if possible. This is where the concept of a lockfile comes into play.
255 ///
256 /// If a summary points at a package id which was previously locked, then we
257 /// override the summary's id itself, as well as all dependencies, to be
258 /// rewritten to the locked versions. This will transform the summary's
259 /// source to a precise source (listed in the locked version) as well as
260 /// transforming all of the dependencies from range requirements on
261 /// imprecise sources to exact requirements on precise sources.
262 ///
263 /// If a summary does not point at a package id which was previously locked,
264 /// or if any dependencies were added and don't have a previously listed
265 /// version, we still want to avoid updating as many dependencies as
266 /// possible to keep the graph stable. In this case we map all of the
267 /// summary's dependencies to be rewritten to a locked version wherever
268 /// possible. If we're unable to map a dependency though, we just pass it on
269 /// through.
270 pub fn lock(&self, summary: Summary) -> Summary {
61a3c68b 271 lock(&self.locked, &self.patches, summary)
816373d9 272 }
fc0e6426
AC
273
274 fn warn_bad_override(&self,
275 override_summary: &Summary,
276 real_summary: &Summary) -> CargoResult<()> {
772e1a17 277 let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>();
fc0e6426
AC
278
279 let boilerplate = "\
280This is currently allowed but is known to produce buggy behavior with spurious
281recompiles and changes to the crate graph. Path overrides unfortunately were
282never intended to support this feature, so for now this message is just a
283warning. In the future, however, this message will become a hard error.
284
285To change the dependency graph via an override it's recommended to use the
286`[replace]` feature of Cargo instead of the path override feature. This is
287documented online at the url below for more information.
288
289http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
290";
291
292 for dep in override_summary.dependencies() {
772e1a17 293 if let Some(i) = real_deps.iter().position(|d| dep == *d) {
fc0e6426
AC
294 real_deps.remove(i);
295 continue
296 }
297 let msg = format!("\
298 path override for crate `{}` has altered the original list of\n\
299 dependencies; the dependency on `{}` was either added or\n\
300 modified to not match the previously resolved version\n\n\
301 {}", override_summary.package_id().name(), dep.name(), boilerplate);
82655b46 302 self.source_config.config().shell().warn(&msg)?;
fc0e6426
AC
303 return Ok(())
304 }
305
23591fe5 306 if let Some(id) = real_deps.get(0) {
fc0e6426
AC
307 let msg = format!("\
308 path override for crate `{}` has altered the original list of
309 dependencies; the dependency on `{}` was removed\n\n
310 {}", override_summary.package_id().name(), id.name(), boilerplate);
82655b46 311 self.source_config.config().shell().warn(&msg)?;
fc0e6426
AC
312 }
313
314 Ok(())
315 }
9b278af1
YKCL
316}
317
2fe0bf83 318impl<'cfg> Registry for PackageRegistry<'cfg> {
842c182e
AC
319 fn query(&mut self,
320 dep: &Dependency,
321 f: &mut FnMut(Summary)) -> CargoResult<()> {
e3835f70
AC
322 let (override_summary, n, to_warn) = {
323 // Look for an override and get ready to query the real source.
23591fe5 324 let override_summary = self.query_overrides(dep)?;
61a3c68b
AC
325
326 // Next up on our list of candidates is to check the `[patch]`
327 // section of the manifest. Here we look through all patches
328 // relevant to the source that `dep` points to, and then we match
329 // name/version. Note that we don't use `dep.matches(..)` because
330 // the patches, by definition, come from a different source.
331 // This means that `dep.matches(..)` will always return false, when
332 // what we really care about is the name/version match.
333 let mut patches = Vec::<Summary>::new();
334 if let Some(extra) = self.patches.get(dep.source_id().url()) {
335 patches.extend(extra.iter().filter(|s| {
336 dep.matches_ignoring_source(s)
337 }).cloned());
338 }
339
340 // A crucial feature of the `[patch]` feature is that we *don't*
341 // query the actual registry if we have a "locked" dependency. A
342 // locked dep basically just means a version constraint of `=a.b.c`,
343 // and because patches take priority over the actual source then if
344 // we have a candidate we're done.
345 if patches.len() == 1 && dep.is_locked() {
346 let patch = patches.remove(0);
347 match override_summary {
348 Some(summary) => (summary, 1, Some(patch)),
349 None => {
350 f(patch);
351 return Ok(())
352 }
353 }
354 } else {
23591fe5 355 if !patches.is_empty() {
61a3c68b
AC
356 debug!("found {} patches with an unlocked dep, \
357 looking at sources", patches.len());
e3835f70
AC
358 }
359
61a3c68b
AC
360 // Ensure the requested source_id is loaded
361 self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| {
362 format!("failed to load source for a dependency \
363 on `{}`", dep.name())
364 })?;
365
366 let source = self.sources.get_mut(dep.source_id());
367 match (override_summary, source) {
368 (Some(_), None) => bail!("override found but no real ones"),
369 (None, None) => return Ok(()),
370
371 // If we don't have an override then we just ship
372 // everything upstairs after locking the summary
373 (None, Some(source)) => {
374 for patch in patches.iter() {
375 f(patch.clone());
376 }
377
378 // Our sources shouldn't ever come back to us with two
379 // summaries that have the same version. We could,
380 // however, have an `[patch]` section which is in use
381 // to override a version in the registry. This means
382 // that if our `summary` in this loop has the same
383 // version as something in `patches` that we've
384 // already selected, then we skip this `summary`.
385 let locked = &self.locked;
386 let all_patches = &self.patches;
387 return source.query(dep, &mut |summary| {
388 for patch in patches.iter() {
389 let patch = patch.package_id().version();
390 if summary.package_id().version() == patch {
391 return
392 }
393 }
394 f(lock(locked, all_patches, summary))
395 })
396 }
397
398 // If we have an override summary then we query the source
399 // to sanity check its results. We don't actually use any of
400 // the summaries it gives us though.
401 (Some(override_summary), Some(source)) => {
23591fe5 402 if !patches.is_empty() {
61a3c68b
AC
403 bail!("found patches and a path override")
404 }
405 let mut n = 0;
406 let mut to_warn = None;
407 source.query(dep, &mut |summary| {
408 n += 1;
409 to_warn = Some(summary);
410 })?;
411 (override_summary, n, to_warn)
412 }
e3835f70 413 }
86774bd8 414 }
816373d9
AC
415 };
416
842c182e
AC
417 if n > 1 {
418 bail!("found an override with a non-locked list");
419 } else if let Some(summary) = to_warn {
420 self.warn_bad_override(&override_summary, &summary)?;
421 }
422 f(self.lock(override_summary));
423 Ok(())
8f6d4afb 424 }
5b08b8fe
AC
425
426 fn supports_checksums(&self) -> bool {
427 false
428 }
429
430 fn requires_precise(&self) -> bool {
431 false
432 }
f4d6d021 433}
e465ace4 434
61a3c68b
AC
435fn lock(locked: &LockedMap,
436 patches: &HashMap<Url, Vec<Summary>>,
437 summary: Summary) -> Summary {
e3835f70
AC
438 let pair = locked.get(summary.source_id()).and_then(|map| {
439 map.get(summary.name())
440 }).and_then(|vec| {
441 vec.iter().find(|&&(ref id, _)| id == summary.package_id())
442 });
443
444 trace!("locking summary of {}", summary.package_id());
445
446 // Lock the summary's id if possible
447 let summary = match pair {
448 Some(&(ref precise, _)) => summary.override_id(precise.clone()),
449 None => summary,
450 };
61a3c68b 451 summary.map_dependencies(|dep| {
e3835f70
AC
452 trace!("\t{}/{}/{}", dep.name(), dep.version_req(),
453 dep.source_id());
454
455 // If we've got a known set of overrides for this summary, then
456 // one of a few cases can arise:
457 //
458 // 1. We have a lock entry for this dependency from the same
459 // source as it's listed as coming from. In this case we make
460 // sure to lock to precisely the given package id.
461 //
462 // 2. We have a lock entry for this dependency, but it's from a
463 // different source than what's listed, or the version
464 // requirement has changed. In this case we must discard the
465 // locked version because the dependency needs to be
466 // re-resolved.
467 //
468 // 3. We don't have a lock entry for this dependency, in which
469 // case it was likely an optional dependency which wasn't
470 // included previously so we just pass it through anyway.
471 //
472 // Cases 1/2 are handled by `matches_id` and case 3 is handled by
473 // falling through to the logic below.
474 if let Some(&(_, ref locked_deps)) = pair {
475 let locked = locked_deps.iter().find(|id| dep.matches_id(id));
476 if let Some(locked) = locked {
477 trace!("\tfirst hit on {}", locked);
61a3c68b 478 let mut dep = dep.clone();
e3835f70
AC
479 dep.lock_to(locked);
480 return dep
481 }
482 }
483
484 // If this dependency did not have a locked version, then we query
485 // all known locked packages to see if they match this dependency.
486 // If anything does then we lock it to that and move on.
487 let v = locked.get(dep.source_id()).and_then(|map| {
488 map.get(dep.name())
489 }).and_then(|vec| {
490 vec.iter().find(|&&(ref id, _)| dep.matches_id(id))
491 });
61a3c68b
AC
492 if let Some(&(ref id, _)) = v {
493 trace!("\tsecond hit on {}", id);
494 let mut dep = dep.clone();
495 dep.lock_to(id);
496 return dep
497 }
498
499 // Finally we check to see if any registered patches correspond to
500 // this dependency.
501 let v = patches.get(dep.source_id().url()).map(|vec| {
502 let dep2 = dep.clone();
503 let mut iter = vec.iter().filter(move |s| {
504 dep2.name() == s.package_id().name() &&
505 dep2.version_req().matches(s.package_id().version())
506 });
507 (iter.next(), iter)
508 });
509 if let Some((Some(summary), mut remaining)) = v {
510 assert!(remaining.next().is_none());
511 let patch_source = summary.package_id().source_id();
512 let patch_locked = locked.get(patch_source).and_then(|m| {
513 m.get(summary.package_id().name())
514 }).map(|list| {
515 list.iter().any(|&(ref id, _)| id == summary.package_id())
516 }).unwrap_or(false);
517
518 if patch_locked {
519 trace!("\tthird hit on {}", summary.package_id());
520 let req = VersionReq::exact(summary.package_id().version());
521 let mut dep = dep.clone();
522 dep.set_version_req(req);
e3835f70
AC
523 return dep
524 }
e3835f70 525 }
61a3c68b
AC
526
527 trace!("\tnope, unlocked");
23591fe5 528 dep
e3835f70
AC
529 })
530}
531
e465ace4
TCS
532#[cfg(test)]
533pub mod test {
534 use core::{Summary, Registry, Dependency};
c810424c 535 use util::CargoResult;
e465ace4
TCS
536
537 pub struct RegistryBuilder {
538 summaries: Vec<Summary>,
539 overrides: Vec<Summary>
540 }
541
542 impl RegistryBuilder {
543 pub fn new() -> RegistryBuilder {
ab1cb51f 544 RegistryBuilder { summaries: vec![], overrides: vec![] }
e465ace4
TCS
545 }
546
547 pub fn summary(mut self, summary: Summary) -> RegistryBuilder {
548 self.summaries.push(summary);
549 self
550 }
551
552 pub fn summaries(mut self, summaries: Vec<Summary>) -> RegistryBuilder {
3cdca46b 553 self.summaries.extend(summaries.into_iter());
e465ace4
TCS
554 self
555 }
556
c12a64f9 557 pub fn add_override(mut self, summary: Summary) -> RegistryBuilder {
e465ace4
TCS
558 self.overrides.push(summary);
559 self
560 }
561
562 pub fn overrides(mut self, summaries: Vec<Summary>) -> RegistryBuilder {
3cdca46b 563 self.overrides.extend(summaries.into_iter());
e465ace4
TCS
564 self
565 }
566
567 fn query_overrides(&self, dep: &Dependency) -> Vec<Summary> {
568 self.overrides.iter()
7a2facba 569 .filter(|s| s.name() == dep.name())
e465ace4
TCS
570 .map(|s| s.clone())
571 .collect()
572 }
573 }
574
575 impl Registry for RegistryBuilder {
842c182e
AC
576 fn query(&mut self,
577 dep: &Dependency,
578 f: &mut FnMut(Summary)) -> CargoResult<()> {
55321111 579 debug!("querying; dep={:?}", dep);
e465ace4
TCS
580
581 let overrides = self.query_overrides(dep);
582
583 if overrides.is_empty() {
a389d483
AC
584 for s in self.summaries.iter() {
585 if dep.matches(s) {
586 f(s.clone());
587 }
588 }
589 Ok(())
e465ace4 590 } else {
842c182e
AC
591 for s in overrides {
592 f(s);
593 }
594 Ok(())
e465ace4
TCS
595 }
596 }
5b08b8fe
AC
597
598 fn supports_checksums(&self) -> bool {
599 false
600 }
601
602 fn requires_precise(&self) -> bool {
603 false
604 }
e465ace4
TCS
605 }
606}