]>
Commit | Line | Data |
---|---|---|
f9b2f5e1 | 1 | use std::collections::HashSet; |
d058d24a | 2 | use std::collections::hash_map::HashMap; |
7192e29a | 3 | |
5451f95d | 4 | use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package}; |
4f12a2b5 | 5 | use util::{CargoResult, ChainError, Config, human, profile}; |
f4d6d021 | 6 | |
bacb6be3 | 7 | /// Source of information about a group of packages. |
c7641c24 PK |
8 | /// |
9 | /// See also `core::Source`. | |
f4d6d021 | 10 | pub trait Registry { |
c7641c24 | 11 | /// Attempt to find the packages that match a dependency request. |
9b278af1 | 12 | fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>>; |
8f6d4afb CL |
13 | } |
14 | ||
15 | impl Registry for Vec<Summary> { | |
46f90ba5 | 16 | fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> { |
3b77b2c7 AC |
17 | Ok(self.iter().filter(|summary| dep.matches(*summary)) |
18 | .map(|summary| summary.clone()).collect()) | |
9b278af1 YKCL |
19 | } |
20 | } | |
21 | ||
348c7389 AC |
22 | impl Registry for Vec<Package> { |
23 | fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> { | |
24 | Ok(self.iter().filter(|pkg| dep.matches(pkg.summary())) | |
25 | .map(|pkg| pkg.summary().clone()).collect()) | |
26 | } | |
27 | } | |
28 | ||
816373d9 AC |
29 | /// This structure represents a registry of known packages. It internally |
30 | /// contains a number of `Box<Source>` instances which are used to load a | |
31 | /// `Package` from. | |
32 | /// | |
33 | /// The resolution phase of Cargo uses this to drive knowledge about new | |
34 | /// packages as well as querying for lists of new packages. It is here that | |
bacb6be3 | 35 | /// sources are updated (e.g. network operations) and overrides are |
816373d9 AC |
36 | /// handled. |
37 | /// | |
38 | /// The general idea behind this registry is that it is centered around the | |
bacb6be3 | 39 | /// `SourceMap` structure, contained within which is a mapping of a `SourceId` to |
816373d9 AC |
40 | /// a `Source`. Each `Source` in the map has been updated (using network |
41 | /// operations if necessary) and is ready to be queried for packages. | |
2fe0bf83 AC |
42 | pub struct PackageRegistry<'cfg> { |
43 | sources: SourceMap<'cfg>, | |
44 | config: &'cfg Config, | |
816373d9 AC |
45 | |
46 | // A list of sources which are considered "overrides" which take precedent | |
47 | // when querying for packages. | |
3b77b2c7 | 48 | overrides: Vec<SourceId>, |
6ab93117 AC |
49 | |
50 | // Note that each SourceId does not take into account its `precise` field | |
51 | // when hashing or testing for equality. When adding a new `SourceId`, we | |
52 | // want to avoid duplicates in the `SourceMap` (to prevent re-updating the | |
53 | // same git repo twice for example), but we also want to ensure that the | |
54 | // loaded source is always updated. | |
55 | // | |
56 | // Sources with a `precise` field normally don't need to be updated because | |
57 | // their contents are already on disk, but sources without a `precise` field | |
58 | // almost always need to be updated. If we have a cached `Source` for a | |
59 | // precise `SourceId`, then when we add a new `SourceId` that is not precise | |
60 | // we want to ensure that the underlying source is updated. | |
61 | // | |
62 | // This is basically a long-winded way of saying that we want to know | |
63 | // precisely what the keys of `sources` are, so this is a mapping of key to | |
64 | // what exactly the key is. | |
65 | source_ids: HashMap<SourceId, (SourceId, Kind)>, | |
66 | ||
816373d9 | 67 | locked: HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>, |
9b278af1 YKCL |
68 | } |
69 | ||
abe56727 | 70 | #[derive(PartialEq, Eq, Clone, Copy)] |
6ab93117 AC |
71 | enum Kind { |
72 | Override, | |
73 | Locked, | |
74 | Normal, | |
75 | } | |
76 | ||
2fe0bf83 AC |
77 | impl<'cfg> PackageRegistry<'cfg> { |
78 | pub fn new(config: &'cfg Config) -> PackageRegistry<'cfg> { | |
98322afd | 79 | PackageRegistry { |
5451f95d | 80 | sources: SourceMap::new(), |
6ab93117 | 81 | source_ids: HashMap::new(), |
98322afd | 82 | overrides: vec!(), |
816373d9 AC |
83 | config: config, |
84 | locked: HashMap::new(), | |
98322afd YKCL |
85 | } |
86 | } | |
87 | ||
18e884d8 | 88 | pub fn get(&mut self, package_ids: &[PackageId]) -> CargoResult<Vec<Package>> { |
98854f6f | 89 | trace!("getting packages; sources={}", self.sources.len()); |
98322afd | 90 | |
64ff29ff AC |
91 | // TODO: Only call source with package ID if the package came from the |
92 | // source | |
98322afd YKCL |
93 | let mut ret = Vec::new(); |
94 | ||
aa97b32e | 95 | for (_, source) in self.sources.sources_mut() { |
98322afd YKCL |
96 | try!(source.download(package_ids)); |
97 | let packages = try!(source.get(package_ids)); | |
98 | ||
3cdca46b | 99 | ret.extend(packages.into_iter()); |
98322afd YKCL |
100 | } |
101 | ||
102 | // TODO: Return earlier if fail | |
64ff29ff | 103 | assert!(package_ids.len() == ret.len(), |
55321111 | 104 | "could not get packages from registry; ids={:?}; ret={:?}", |
3b77b2c7 | 105 | package_ids, ret); |
98322afd YKCL |
106 | |
107 | Ok(ret) | |
9b278af1 YKCL |
108 | } |
109 | ||
2fe0bf83 | 110 | pub fn move_sources(self) -> SourceMap<'cfg> { |
5451f95d CL |
111 | self.sources |
112 | } | |
113 | ||
9b278af1 | 114 | fn ensure_loaded(&mut self, namespace: &SourceId) -> CargoResult<()> { |
edc6ebc5 | 115 | match self.source_ids.get(namespace) { |
6ab93117 AC |
116 | // We've previously loaded this source, and we've already locked it, |
117 | // so we're not allowed to change it even if `namespace` has a | |
118 | // slightly different precise version listed. | |
f162aed2 AC |
119 | Some(&(_, Kind::Locked)) => { |
120 | debug!("load/locked {}", namespace); | |
121 | return Ok(()) | |
122 | } | |
6ab93117 AC |
123 | |
124 | // If the previous source was not a precise source, then we can be | |
125 | // sure that it's already been updated if we've already loaded it. | |
7a2facba | 126 | Some(&(ref previous, _)) if previous.precise().is_none() => { |
f162aed2 | 127 | debug!("load/precise {}", namespace); |
6ab93117 AC |
128 | return Ok(()) |
129 | } | |
130 | ||
131 | // If the previous source has the same precise version as we do, | |
132 | // then we're done, otherwise we need to need to move forward | |
133 | // updating this source. | |
134 | Some(&(ref previous, _)) => { | |
7a2facba | 135 | if previous.precise() == namespace.precise() { |
f162aed2 | 136 | debug!("load/match {}", namespace); |
6ab93117 AC |
137 | return Ok(()) |
138 | } | |
f162aed2 AC |
139 | debug!("load/mismatch {}", namespace); |
140 | } | |
141 | None => { | |
142 | debug!("load/missing {}", namespace); | |
6ab93117 | 143 | } |
6ab93117 | 144 | } |
5451f95d | 145 | |
38d14a59 | 146 | try!(self.load(namespace, Kind::Normal)); |
9b278af1 YKCL |
147 | Ok(()) |
148 | } | |
149 | ||
c94648ba AC |
150 | pub fn add_sources(&mut self, ids: &[SourceId]) -> CargoResult<()> { |
151 | for id in ids.iter() { | |
38d14a59 | 152 | try!(self.load(id, Kind::Locked)); |
1a35097a AC |
153 | } |
154 | Ok(()) | |
155 | } | |
156 | ||
3b77b2c7 AC |
157 | pub fn add_overrides(&mut self, ids: Vec<SourceId>) -> CargoResult<()> { |
158 | for id in ids.iter() { | |
38d14a59 | 159 | try!(self.load(id, Kind::Override)); |
3b77b2c7 | 160 | } |
e465ace4 TCS |
161 | Ok(()) |
162 | } | |
163 | ||
816373d9 | 164 | pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) { |
6e6dec8a AC |
165 | let sub_map = self.locked.entry(id.source_id().clone()) |
166 | .or_insert(HashMap::new()); | |
167 | let sub_vec = sub_map.entry(id.name().to_string()) | |
168 | .or_insert(Vec::new()); | |
816373d9 AC |
169 | sub_vec.push((id, deps)); |
170 | } | |
171 | ||
6ab93117 | 172 | fn load(&mut self, source_id: &SourceId, kind: Kind) -> CargoResult<()> { |
f2aa5b4c | 173 | (|| { |
e465ace4 | 174 | let mut source = source_id.load(self.config); |
9b278af1 | 175 | |
f137281b | 176 | // Ensure the source has fetched all necessary remote data. |
4f12a2b5 | 177 | let p = profile::start(format!("updating: {}", source_id)); |
f137281b | 178 | try!(source.update()); |
4f12a2b5 | 179 | drop(p); |
9b278af1 | 180 | |
38d14a59 | 181 | if kind == Kind::Override { |
3b77b2c7 | 182 | self.overrides.push(source_id.clone()); |
f137281b | 183 | } |
98322afd | 184 | |
f137281b | 185 | // Save off the source |
3b77b2c7 | 186 | self.sources.insert(source_id, source); |
6ab93117 | 187 | self.source_ids.insert(source_id.clone(), (source_id.clone(), kind)); |
e465ace4 | 188 | |
f137281b | 189 | Ok(()) |
e465ace4 | 190 | }).chain_error(|| human(format!("Unable to update {}", source_id))) |
9b278af1 | 191 | } |
46f90ba5 | 192 | |
3b77b2c7 AC |
193 | fn query_overrides(&mut self, dep: &Dependency) |
194 | -> CargoResult<Vec<Summary>> { | |
7192e29a | 195 | let mut seen = HashSet::new(); |
3b77b2c7 AC |
196 | let mut ret = Vec::new(); |
197 | for s in self.overrides.iter() { | |
198 | let src = self.sources.get_mut(s).unwrap(); | |
7a2facba | 199 | let dep = Dependency::new_override(dep.name(), s); |
03e3dba1 | 200 | ret.extend(try!(src.query(&dep)).into_iter().filter(|s| { |
7a2facba | 201 | seen.insert(s.name().to_string()) |
7192e29a | 202 | })); |
3b77b2c7 AC |
203 | } |
204 | Ok(ret) | |
46f90ba5 | 205 | } |
816373d9 AC |
206 | |
207 | // This function is used to transform a summary to another locked summary if | |
b186d2f5 | 208 | // possible. This is where the concept of a lockfile comes into play. |
816373d9 AC |
209 | // |
210 | // If a summary points at a package id which was previously locked, then we | |
bacb6be3 | 211 | // override the summary's id itself, as well as all dependencies, to be |
816373d9 AC |
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 imprecise | |
215 | // sources to exact requirements on precise sources. | |
216 | // | |
217 | // If a summary does not point at a package id which was previously locked, | |
218 | // we still want to avoid updating as many dependencies as possible to keep | |
219 | // the graph stable. In this case we map all of the summary's dependencies | |
220 | // to be rewritten to a locked version wherever possible. If we're unable to | |
221 | // map a dependency though, we just pass it on through. | |
222 | fn lock(&self, summary: Summary) -> Summary { | |
7a2facba AC |
223 | let pair = self.locked.get(summary.source_id()).and_then(|map| { |
224 | map.get(summary.name()) | |
816373d9 | 225 | }).and_then(|vec| { |
7a2facba | 226 | vec.iter().find(|&&(ref id, _)| id == summary.package_id()) |
816373d9 AC |
227 | }); |
228 | ||
229 | // Lock the summary's id if possible | |
230 | let summary = match pair { | |
231 | Some(&(ref precise, _)) => summary.override_id(precise.clone()), | |
232 | None => summary, | |
233 | }; | |
234 | summary.map_dependencies(|dep| { | |
235 | match pair { | |
2ba5c5ec AC |
236 | // If we've got a known set of overrides for this summary, then |
237 | // one of a few cases can arise: | |
238 | // | |
239 | // 1. We have a lock entry for this dependency from the same | |
bacb6be3 | 240 | // source as it's listed as coming from. In this case we make |
2ba5c5ec AC |
241 | // sure to lock to precisely the given package id. |
242 | // | |
243 | // 2. We have a lock entry for this dependency, but it's from a | |
493d3108 AC |
244 | // different source than what's listed, or the version |
245 | // requirement has changed. In this case we must discard the | |
246 | // locked version because the dependency needs to be | |
247 | // re-resolved. | |
2ba5c5ec AC |
248 | // |
249 | // 3. We don't have a lock entry for this dependency, in which | |
250 | // case it was likely an optional dependency which wasn't | |
251 | // included previously so we just pass it through anyway. | |
816373d9 | 252 | Some(&(_, ref deps)) => { |
7a2facba | 253 | match deps.iter().find(|d| d.name() == dep.name()) { |
2ba5c5ec | 254 | Some(lock) => { |
493d3108 | 255 | if dep.matches_id(lock) { |
2ba5c5ec AC |
256 | dep.lock_to(lock) |
257 | } else { | |
258 | dep | |
259 | } | |
260 | } | |
816373d9 AC |
261 | None => dep, |
262 | } | |
263 | } | |
264 | ||
265 | // If this summary did not have a locked version, then we query | |
266 | // all known locked packages to see if they match this | |
267 | // dependency. If anything does then we lock it to that and move | |
268 | // on. | |
269 | None => { | |
7a2facba AC |
270 | let v = self.locked.get(dep.source_id()).and_then(|map| { |
271 | map.get(dep.name()) | |
816373d9 AC |
272 | }).and_then(|vec| { |
273 | vec.iter().find(|&&(ref id, _)| dep.matches_id(id)) | |
274 | }); | |
275 | match v { | |
276 | Some(&(ref id, _)) => dep.lock_to(id), | |
277 | None => dep | |
278 | } | |
279 | } | |
280 | } | |
281 | }) | |
282 | } | |
9b278af1 YKCL |
283 | } |
284 | ||
2fe0bf83 | 285 | impl<'cfg> Registry for PackageRegistry<'cfg> { |
9b278af1 | 286 | fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> { |
3b77b2c7 | 287 | let overrides = try!(self.query_overrides(dep)); |
9b278af1 | 288 | |
816373d9 | 289 | let ret = if overrides.len() == 0 { |
e465ace4 | 290 | // Ensure the requested source_id is loaded |
7a2facba | 291 | try!(self.ensure_loaded(dep.source_id())); |
3b77b2c7 | 292 | let mut ret = Vec::new(); |
aa97b32e | 293 | for (id, src) in self.sources.sources_mut() { |
7a2facba | 294 | if id == dep.source_id() { |
aa97b32e AC |
295 | ret.extend(try!(src.query(dep)).into_iter()); |
296 | } | |
3b77b2c7 | 297 | } |
816373d9 | 298 | ret |
9b278af1 | 299 | } else { |
816373d9 AC |
300 | overrides |
301 | }; | |
302 | ||
303 | // post-process all returned summaries to ensure that we lock all | |
304 | // relevant summaries to the right versions and sources | |
305 | Ok(ret.into_iter().map(|summary| self.lock(summary)).collect()) | |
8f6d4afb | 306 | } |
f4d6d021 | 307 | } |
e465ace4 TCS |
308 | |
309 | #[cfg(test)] | |
310 | pub mod test { | |
311 | use core::{Summary, Registry, Dependency}; | |
312 | use util::{CargoResult}; | |
313 | ||
314 | pub struct RegistryBuilder { | |
315 | summaries: Vec<Summary>, | |
316 | overrides: Vec<Summary> | |
317 | } | |
318 | ||
319 | impl RegistryBuilder { | |
320 | pub fn new() -> RegistryBuilder { | |
321 | RegistryBuilder { summaries: vec!(), overrides: vec!() } | |
322 | } | |
323 | ||
324 | pub fn summary(mut self, summary: Summary) -> RegistryBuilder { | |
325 | self.summaries.push(summary); | |
326 | self | |
327 | } | |
328 | ||
329 | pub fn summaries(mut self, summaries: Vec<Summary>) -> RegistryBuilder { | |
3cdca46b | 330 | self.summaries.extend(summaries.into_iter()); |
e465ace4 TCS |
331 | self |
332 | } | |
333 | ||
c12a64f9 | 334 | pub fn add_override(mut self, summary: Summary) -> RegistryBuilder { |
e465ace4 TCS |
335 | self.overrides.push(summary); |
336 | self | |
337 | } | |
338 | ||
339 | pub fn overrides(mut self, summaries: Vec<Summary>) -> RegistryBuilder { | |
3cdca46b | 340 | self.overrides.extend(summaries.into_iter()); |
e465ace4 TCS |
341 | self |
342 | } | |
343 | ||
344 | fn query_overrides(&self, dep: &Dependency) -> Vec<Summary> { | |
345 | self.overrides.iter() | |
7a2facba | 346 | .filter(|s| s.name() == dep.name()) |
e465ace4 TCS |
347 | .map(|s| s.clone()) |
348 | .collect() | |
349 | } | |
350 | } | |
351 | ||
352 | impl Registry for RegistryBuilder { | |
353 | fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> { | |
55321111 | 354 | debug!("querying; dep={:?}", dep); |
e465ace4 TCS |
355 | |
356 | let overrides = self.query_overrides(dep); | |
357 | ||
358 | if overrides.is_empty() { | |
359 | self.summaries.query(dep) | |
360 | } else { | |
361 | Ok(overrides) | |
362 | } | |
363 | } | |
364 | } | |
365 | } |