]>
Commit | Line | Data |
---|---|---|
842c182e | 1 | use std::cell::RefCell; |
fc0e6426 | 2 | use std::collections::HashMap; |
7192e29a | 3 | |
5451f95d | 4 | use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package}; |
2d25fcac | 5 | use core::PackageSet; |
c7de4859 | 6 | use util::{Config, profile}; |
e95044e3 | 7 | use util::errors::{CargoResult, CargoResultExt}; |
8214bb95 | 8 | use sources::config::SourceConfigMap; |
f4d6d021 | 9 | |
bacb6be3 | 10 | /// Source of information about a group of packages. |
c7641c24 PK |
11 | /// |
12 | /// See also `core::Source`. | |
f4d6d021 | 13 | pub 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 | ||
34 | impl 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 | 45 | impl 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 | 58 | impl<'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 | 79 | pub 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 |
108 | enum Kind { |
109 | Override, | |
110 | Locked, | |
111 | Normal, | |
112 | } | |
113 | ||
2fe0bf83 | 114 | impl<'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 = "\ | |
322 | This is currently allowed but is known to produce buggy behavior with spurious | |
323 | recompiles and changes to the crate graph. Path overrides unfortunately were | |
324 | never intended to support this feature, so for now this message is just a | |
325 | warning. In the future, however, this message will become a hard error. | |
326 | ||
327 | To 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 | |
329 | documented online at the url below for more information. | |
330 | ||
331 | http://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 | 361 | impl<'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)] | |
418 | pub 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 | } |