]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/registry.rs
Remove DependencyInner as a public API
[cargo.git] / src / cargo / core / registry.rs
CommitLineData
842c182e 1use std::cell::RefCell;
fc0e6426 2use std::collections::HashMap;
7192e29a 3
5451f95d 4use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package};
2d25fcac 5use core::PackageSet;
c7de4859 6use util::{Config, profile};
e95044e3 7use util::errors::{CargoResult, CargoResultExt};
8214bb95 8use sources::config::SourceConfigMap;
f4d6d021 9
bacb6be3 10/// Source of information about a group of packages.
c7641c24
PK
11///
12/// See also `core::Source`.
f4d6d021 13pub trait Registry {
c7641c24 14 /// Attempt to find the packages that match a dependency request.
842c182e
AC
15 fn query(&mut self,
16 dep: &Dependency,
17 f: &mut FnMut(Summary)) -> CargoResult<()>;
18
19 fn query_vec(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
20 let mut ret = Vec::new();
21 self.query(dep, &mut |s| ret.push(s))?;
22 Ok(ret)
23 }
5430db61
AC
24
25 /// Returns whether or not this registry will return summaries with
26 /// checksums listed.
27 ///
28 /// By default, registries do not support checksums.
29 fn supports_checksums(&self) -> bool {
30 false
31 }
8f6d4afb
CL
32}
33
34impl Registry for Vec<Summary> {
842c182e
AC
35 fn query(&mut self,
36 dep: &Dependency,
37 f: &mut FnMut(Summary)) -> CargoResult<()> {
38 for summary in self.iter().filter(|summary| dep.matches(*summary)) {
39 f(summary.clone());
40 }
41 Ok(())
9b278af1
YKCL
42 }
43}
44
348c7389 45impl Registry for Vec<Package> {
842c182e
AC
46 fn query(&mut self,
47 dep: &Dependency,
48 f: &mut FnMut(Summary)) -> CargoResult<()> {
49 for summary in self.iter()
50 .map(|p| p.summary())
51 .filter(|summary| dep.matches(*summary)) {
52 f(summary.clone());
53 }
54 Ok(())
348c7389
AC
55 }
56}
57
7ff9cbeb 58impl<'a, T: ?Sized + Registry + 'a> Registry for Box<T> {
842c182e
AC
59 fn query(&mut self,
60 dep: &Dependency,
61 f: &mut FnMut(Summary)) -> CargoResult<()> {
62 (**self).query(dep, f)
7ff9cbeb
AC
63 }
64}
65
816373d9
AC
66/// This structure represents a registry of known packages. It internally
67/// contains a number of `Box<Source>` instances which are used to load a
68/// `Package` from.
69///
70/// The resolution phase of Cargo uses this to drive knowledge about new
71/// packages as well as querying for lists of new packages. It is here that
bacb6be3 72/// sources are updated (e.g. network operations) and overrides are
816373d9
AC
73/// handled.
74///
75/// The general idea behind this registry is that it is centered around the
bacb6be3 76/// `SourceMap` structure, contained within which is a mapping of a `SourceId` to
816373d9
AC
77/// a `Source`. Each `Source` in the map has been updated (using network
78/// operations if necessary) and is ready to be queried for packages.
2fe0bf83 79pub struct PackageRegistry<'cfg> {
842c182e 80 sources: RefCell<SourceMap<'cfg>>,
816373d9
AC
81
82 // A list of sources which are considered "overrides" which take precedent
83 // when querying for packages.
3b77b2c7 84 overrides: Vec<SourceId>,
6ab93117
AC
85
86 // Note that each SourceId does not take into account its `precise` field
87 // when hashing or testing for equality. When adding a new `SourceId`, we
88 // want to avoid duplicates in the `SourceMap` (to prevent re-updating the
89 // same git repo twice for example), but we also want to ensure that the
90 // loaded source is always updated.
91 //
92 // Sources with a `precise` field normally don't need to be updated because
93 // their contents are already on disk, but sources without a `precise` field
94 // almost always need to be updated. If we have a cached `Source` for a
95 // precise `SourceId`, then when we add a new `SourceId` that is not precise
96 // we want to ensure that the underlying source is updated.
97 //
98 // This is basically a long-winded way of saying that we want to know
99 // precisely what the keys of `sources` are, so this is a mapping of key to
100 // what exactly the key is.
101 source_ids: HashMap<SourceId, (SourceId, Kind)>,
102
816373d9 103 locked: HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>,
8214bb95 104 source_config: SourceConfigMap<'cfg>,
9b278af1
YKCL
105}
106
abe56727 107#[derive(PartialEq, Eq, Clone, Copy)]
6ab93117
AC
108enum Kind {
109 Override,
110 Locked,
111 Normal,
112}
113
2fe0bf83 114impl<'cfg> PackageRegistry<'cfg> {
8214bb95 115 pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> {
82655b46 116 let source_config = SourceConfigMap::new(config)?;
8214bb95 117 Ok(PackageRegistry {
842c182e 118 sources: RefCell::new(SourceMap::new()),
6ab93117 119 source_ids: HashMap::new(),
54d738b0 120 overrides: Vec::new(),
8214bb95 121 source_config: source_config,
816373d9 122 locked: HashMap::new(),
8214bb95 123 })
98322afd
YKCL
124 }
125
f9945926 126 pub fn get(self, package_ids: &[PackageId]) -> PackageSet<'cfg> {
842c182e
AC
127 let sources = self.sources.into_inner();
128 trace!("getting packages; sources={}", sources.len());
129 PackageSet::new(package_ids, sources)
5451f95d
CL
130 }
131
f0647ea2 132 fn ensure_loaded(&mut self, namespace: &SourceId, kind: Kind) -> CargoResult<()> {
edc6ebc5 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.
f162aed2
AC
137 Some(&(_, Kind::Locked)) => {
138 debug!("load/locked {}", namespace);
139 return Ok(())
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.
7a2facba 144 Some(&(ref previous, _)) if previous.precise().is_none() => {
f162aed2 145 debug!("load/precise {}", namespace);
6ab93117
AC
146 return Ok(())
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.
152 Some(&(ref previous, _)) => {
7a2facba 153 if previous.precise() == namespace.precise() {
f162aed2 154 debug!("load/match {}", namespace);
6ab93117
AC
155 return Ok(())
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
c94648ba
AC
168 pub fn add_sources(&mut self, ids: &[SourceId]) -> CargoResult<()> {
169 for id in ids.iter() {
82655b46 170 self.ensure_loaded(id, Kind::Locked)?;
1a35097a
AC
171 }
172 Ok(())
173 }
174
b02023ee
AK
175 pub fn add_preloaded(&mut self, source: Box<Source + 'cfg>) {
176 self.add_source(source, Kind::Locked);
bc60f64b
AC
177 }
178
68c6f2b0 179 fn add_source(&mut self, source: Box<Source + 'cfg>, kind: Kind) {
b02023ee 180 let id = source.source_id().clone();
842c182e 181 self.sources.get_mut().insert(source);
b02023ee 182 self.source_ids.insert(id.clone(), (id, kind));
bc60f64b
AC
183 }
184
b02023ee
AK
185 pub fn add_override(&mut self, source: Box<Source + 'cfg>) {
186 self.overrides.push(source.source_id().clone());
187 self.add_source(source, Kind::Override);
e465ace4
TCS
188 }
189
816373d9 190 pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {
155dee54
AC
191 trace!("register_lock: {}", id);
192 for dep in deps.iter() {
193 trace!("\t-> {}", dep);
194 }
6e6dec8a
AC
195 let sub_map = self.locked.entry(id.source_id().clone())
196 .or_insert(HashMap::new());
197 let sub_vec = sub_map.entry(id.name().to_string())
198 .or_insert(Vec::new());
816373d9
AC
199 sub_vec.push((id, deps));
200 }
201
6ab93117 202 fn load(&mut self, source_id: &SourceId, kind: Kind) -> CargoResult<()> {
f2aa5b4c 203 (|| {
82655b46 204 let source = self.source_config.load(source_id)?;
b02023ee 205 assert_eq!(source.source_id(), source_id);
8214bb95 206
38d14a59 207 if kind == Kind::Override {
3b77b2c7 208 self.overrides.push(source_id.clone());
f137281b 209 }
b02023ee 210 self.add_source(source, kind);
e465ace4 211
18e59304
AC
212 // Ensure the source has fetched all necessary remote data.
213 let _p = profile::start(format!("updating: {}", source_id));
842c182e 214 self.sources.get_mut().get_mut(source_id).unwrap().update()
c7de4859 215 })().chain_err(|| format!("Unable to update {}", source_id))
9b278af1 216 }
46f90ba5 217
3b77b2c7 218 fn query_overrides(&mut self, dep: &Dependency)
fc0e6426 219 -> CargoResult<Option<Summary>> {
3b77b2c7 220 for s in self.overrides.iter() {
842c182e 221 let src = self.sources.get_mut().get_mut(s).unwrap();
7a2facba 222 let dep = Dependency::new_override(dep.name(), s);
842c182e 223 let mut results = src.query_vec(&dep)?;
fc0e6426
AC
224 if results.len() > 0 {
225 return Ok(Some(results.remove(0)))
226 }
3b77b2c7 227 }
fc0e6426 228 Ok(None)
46f90ba5 229 }
816373d9 230
c0306a8a
AC
231 /// This function is used to transform a summary to another locked summary
232 /// if possible. This is where the concept of a lockfile comes into play.
233 ///
234 /// If a summary points at a package id which was previously locked, then we
235 /// override the summary's id itself, as well as all dependencies, to be
236 /// rewritten to the locked versions. This will transform the summary's
237 /// source to a precise source (listed in the locked version) as well as
238 /// transforming all of the dependencies from range requirements on
239 /// imprecise sources to exact requirements on precise sources.
240 ///
241 /// If a summary does not point at a package id which was previously locked,
242 /// or if any dependencies were added and don't have a previously listed
243 /// version, we still want to avoid updating as many dependencies as
244 /// possible to keep the graph stable. In this case we map all of the
245 /// summary's dependencies to be rewritten to a locked version wherever
246 /// possible. If we're unable to map a dependency though, we just pass it on
247 /// through.
248 pub fn lock(&self, summary: Summary) -> Summary {
7a2facba
AC
249 let pair = self.locked.get(summary.source_id()).and_then(|map| {
250 map.get(summary.name())
816373d9 251 }).and_then(|vec| {
7a2facba 252 vec.iter().find(|&&(ref id, _)| id == summary.package_id())
816373d9
AC
253 });
254
155dee54
AC
255 trace!("locking summary of {}", summary.package_id());
256
816373d9
AC
257 // Lock the summary's id if possible
258 let summary = match pair {
259 Some(&(ref precise, _)) => summary.override_id(precise.clone()),
260 None => summary,
261 };
60aadc58 262 summary.map_dependencies(|mut dep| {
155dee54
AC
263 trace!("\t{}/{}/{}", dep.name(), dep.version_req(),
264 dep.source_id());
265
c0306a8a
AC
266 // If we've got a known set of overrides for this summary, then
267 // one of a few cases can arise:
268 //
269 // 1. We have a lock entry for this dependency from the same
270 // source as it's listed as coming from. In this case we make
271 // sure to lock to precisely the given package id.
272 //
273 // 2. We have a lock entry for this dependency, but it's from a
274 // different source than what's listed, or the version
275 // requirement has changed. In this case we must discard the
276 // locked version because the dependency needs to be
277 // re-resolved.
278 //
279 // 3. We don't have a lock entry for this dependency, in which
280 // case it was likely an optional dependency which wasn't
281 // included previously so we just pass it through anyway.
282 //
283 // Cases 1/2 are handled by `matches_id` and case 3 is handled by
284 // falling through to the logic below.
285 if let Some(&(_, ref locked_deps)) = pair {
286 let locked = locked_deps.iter().find(|id| dep.matches_id(id));
287 if let Some(locked) = locked {
155dee54 288 trace!("\tfirst hit on {}", locked);
60aadc58
AC
289 dep.lock_to(locked);
290 return dep
816373d9 291 }
c0306a8a 292 }
816373d9 293
c0306a8a
AC
294 // If this dependency did not have a locked version, then we query
295 // all known locked packages to see if they match this dependency.
296 // If anything does then we lock it to that and move on.
297 let v = self.locked.get(dep.source_id()).and_then(|map| {
298 map.get(dep.name())
299 }).and_then(|vec| {
300 vec.iter().find(|&&(ref id, _)| dep.matches_id(id))
301 });
302 match v {
155dee54
AC
303 Some(&(ref id, _)) => {
304 trace!("\tsecond hit on {}", id);
60aadc58
AC
305 dep.lock_to(id);
306 return dep
155dee54
AC
307 }
308 None => {
309 trace!("\tremaining unlocked");
310 dep
311 }
816373d9
AC
312 }
313 })
314 }
fc0e6426
AC
315
316 fn warn_bad_override(&self,
317 override_summary: &Summary,
318 real_summary: &Summary) -> CargoResult<()> {
772e1a17 319 let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>();
fc0e6426
AC
320
321 let boilerplate = "\
322This is currently allowed but is known to produce buggy behavior with spurious
323recompiles and changes to the crate graph. Path overrides unfortunately were
324never intended to support this feature, so for now this message is just a
325warning. In the future, however, this message will become a hard error.
326
327To change the dependency graph via an override it's recommended to use the
328`[replace]` feature of Cargo instead of the path override feature. This is
329documented online at the url below for more information.
330
331http://doc.crates.io/specifying-dependencies.html#overriding-dependencies
332";
333
334 for dep in override_summary.dependencies() {
772e1a17 335 if let Some(i) = real_deps.iter().position(|d| dep == *d) {
fc0e6426
AC
336 real_deps.remove(i);
337 continue
338 }
339 let msg = format!("\
340 path override for crate `{}` has altered the original list of\n\
341 dependencies; the dependency on `{}` was either added or\n\
342 modified to not match the previously resolved version\n\n\
343 {}", override_summary.package_id().name(), dep.name(), boilerplate);
82655b46 344 self.source_config.config().shell().warn(&msg)?;
fc0e6426
AC
345 return Ok(())
346 }
347
348 for id in real_deps {
349 let msg = format!("\
350 path override for crate `{}` has altered the original list of
351 dependencies; the dependency on `{}` was removed\n\n
352 {}", override_summary.package_id().name(), id.name(), boilerplate);
82655b46 353 self.source_config.config().shell().warn(&msg)?;
fc0e6426
AC
354 return Ok(())
355 }
356
357 Ok(())
358 }
9b278af1
YKCL
359}
360
2fe0bf83 361impl<'cfg> Registry for PackageRegistry<'cfg> {
842c182e
AC
362 fn query(&mut self,
363 dep: &Dependency,
364 f: &mut FnMut(Summary)) -> CargoResult<()> {
fc0e6426 365 // Ensure the requested source_id is loaded
e95044e3 366 self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| {
c7de4859 367 format!("failed to load source for a dependency \
368 on `{}`", dep.name())
82655b46 369 })?;
fc0e6426 370
842c182e 371 // Look for an override and get ready to query the real source
82655b46 372 let override_summary = self.query_overrides(&dep)?;
842c182e
AC
373 let mut sources = self.sources.borrow_mut();
374 let source = match sources.get_mut(dep.source_id()) {
375 Some(src) => src,
376 None => {
377 if override_summary.is_some() {
378 bail!("override found but no real ones");
379 }
380 return Ok(())
381 }
fc0e6426
AC
382 };
383
842c182e
AC
384 // Query the real source, keeping track of some extra info. If we've got
385 // an override we don't actually ship up summaries. If we do ship up
386 // summaries though we be sure to postprocess them to a locked version
387 // to assist with lockfiles and conservative updates.
388 let mut n = 0;
389 let mut to_warn = None;
390 source.query(dep, &mut |summary| {
391 n += 1;
392 if override_summary.is_none() {
393 f(self.lock(summary))
394 } else {
395 to_warn = Some(summary);
3b77b2c7 396 }
842c182e
AC
397 })?;
398
399 // After all that's said and done we need to check the override summary
400 // itself. If present we'll do some more validation and yield it up,
401 // otherwise we're done.
402 let override_summary = match override_summary {
403 Some(s) => s,
404 None => return Ok(())
816373d9
AC
405 };
406
842c182e
AC
407 if n > 1 {
408 bail!("found an override with a non-locked list");
409 } else if let Some(summary) = to_warn {
410 self.warn_bad_override(&override_summary, &summary)?;
411 }
412 f(self.lock(override_summary));
413 Ok(())
8f6d4afb 414 }
f4d6d021 415}
e465ace4
TCS
416
417#[cfg(test)]
418pub mod test {
419 use core::{Summary, Registry, Dependency};
c810424c 420 use util::CargoResult;
e465ace4
TCS
421
422 pub struct RegistryBuilder {
423 summaries: Vec<Summary>,
424 overrides: Vec<Summary>
425 }
426
427 impl RegistryBuilder {
428 pub fn new() -> RegistryBuilder {
ab1cb51f 429 RegistryBuilder { summaries: vec![], overrides: vec![] }
e465ace4
TCS
430 }
431
432 pub fn summary(mut self, summary: Summary) -> RegistryBuilder {
433 self.summaries.push(summary);
434 self
435 }
436
437 pub fn summaries(mut self, summaries: Vec<Summary>) -> RegistryBuilder {
3cdca46b 438 self.summaries.extend(summaries.into_iter());
e465ace4
TCS
439 self
440 }
441
c12a64f9 442 pub fn add_override(mut self, summary: Summary) -> RegistryBuilder {
e465ace4
TCS
443 self.overrides.push(summary);
444 self
445 }
446
447 pub fn overrides(mut self, summaries: Vec<Summary>) -> RegistryBuilder {
3cdca46b 448 self.overrides.extend(summaries.into_iter());
e465ace4
TCS
449 self
450 }
451
452 fn query_overrides(&self, dep: &Dependency) -> Vec<Summary> {
453 self.overrides.iter()
7a2facba 454 .filter(|s| s.name() == dep.name())
e465ace4
TCS
455 .map(|s| s.clone())
456 .collect()
457 }
458 }
459
460 impl Registry for RegistryBuilder {
842c182e
AC
461 fn query(&mut self,
462 dep: &Dependency,
463 f: &mut FnMut(Summary)) -> CargoResult<()> {
55321111 464 debug!("querying; dep={:?}", dep);
e465ace4
TCS
465
466 let overrides = self.query_overrides(dep);
467
468 if overrides.is_empty() {
842c182e 469 self.summaries.query(dep, f)
e465ace4 470 } else {
842c182e
AC
471 for s in overrides {
472 f(s);
473 }
474 Ok(())
e465ace4
TCS
475 }
476 }
477 }
478}