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