]>
Commit | Line | Data |
---|---|---|
fc0e6426 | 1 | use std::collections::HashMap; |
7192e29a | 2 | |
61a3c68b AC |
3 | use semver::VersionReq; |
4 | use url::Url; | |
5 | ||
2d25fcac | 6 | use core::PackageSet; |
e5a11190 | 7 | use core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary}; |
8214bb95 | 8 | use sources::config::SourceConfigMap; |
e5a11190 E |
9 | use util::errors::{CargoResult, CargoResultExt}; |
10 | use util::{profile, Config}; | |
f4d6d021 | 11 | |
bacb6be3 | 12 | /// Source of information about a group of packages. |
c7641c24 PK |
13 | /// |
14 | /// See also `core::Source`. | |
f4d6d021 | 15 | pub trait Registry { |
c7641c24 | 16 | /// Attempt to find the packages that match a dependency request. |
84cc3d8b | 17 | fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()>; |
842c182e | 18 | |
84cc3d8b | 19 | fn query_vec(&mut self, dep: &Dependency, fuzzy: bool) -> CargoResult<Vec<Summary>> { |
842c182e | 20 | let mut ret = Vec::new(); |
84cc3d8b | 21 | self.query(dep, &mut |s| ret.push(s), fuzzy)?; |
842c182e AC |
22 | Ok(ret) |
23 | } | |
20cfb41e | 24 | |
e5a11190 E |
25 | fn describe_source(&self, source: SourceId) -> String; |
26 | fn is_replaced(&self, source: SourceId) -> bool; | |
8f6d4afb CL |
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 | 42 | pub struct PackageRegistry<'cfg> { |
c94804bd | 43 | config: &'cfg Config, |
e3835f70 | 44 | sources: SourceMap<'cfg>, |
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 | ||
e3835f70 | 67 | locked: LockedMap, |
8214bb95 | 68 | source_config: SourceConfigMap<'cfg>, |
eebddd37 | 69 | |
61a3c68b | 70 | patches: HashMap<Url, Vec<Summary>>, |
eebddd37 AC |
71 | patches_locked: bool, |
72 | patches_available: HashMap<Url, Vec<PackageId>>, | |
9b278af1 YKCL |
73 | } |
74 | ||
e3835f70 AC |
75 | type LockedMap = HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>; |
76 | ||
abe56727 | 77 | #[derive(PartialEq, Eq, Clone, Copy)] |
6ab93117 AC |
78 | enum Kind { |
79 | Override, | |
80 | Locked, | |
81 | Normal, | |
82 | } | |
83 | ||
2fe0bf83 | 84 | impl<'cfg> PackageRegistry<'cfg> { |
8214bb95 | 85 | pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> { |
82655b46 | 86 | let source_config = SourceConfigMap::new(config)?; |
8214bb95 | 87 | Ok(PackageRegistry { |
c94804bd | 88 | config, |
e3835f70 | 89 | sources: SourceMap::new(), |
6ab93117 | 90 | source_ids: HashMap::new(), |
54d738b0 | 91 | overrides: Vec::new(), |
0247dc42 | 92 | source_config, |
816373d9 | 93 | locked: HashMap::new(), |
61a3c68b | 94 | patches: HashMap::new(), |
eebddd37 AC |
95 | patches_locked: false, |
96 | patches_available: HashMap::new(), | |
8214bb95 | 97 | }) |
98322afd YKCL |
98 | } |
99 | ||
468f243e | 100 | pub fn get(self, package_ids: &[PackageId]) -> CargoResult<PackageSet<'cfg>> { |
e3835f70 | 101 | trace!("getting packages; sources={}", self.sources.len()); |
c94804bd | 102 | PackageSet::new(package_ids, self.sources, self.config) |
5451f95d CL |
103 | } |
104 | ||
e5a11190 E |
105 | fn ensure_loaded(&mut self, namespace: SourceId, kind: Kind) -> CargoResult<()> { |
106 | match self.source_ids.get(&namespace) { | |
6ab93117 AC |
107 | // We've previously loaded this source, and we've already locked it, |
108 | // so we're not allowed to change it even if `namespace` has a | |
109 | // slightly different precise version listed. | |
f162aed2 AC |
110 | Some(&(_, Kind::Locked)) => { |
111 | debug!("load/locked {}", namespace); | |
1e682848 | 112 | return Ok(()); |
f162aed2 | 113 | } |
6ab93117 AC |
114 | |
115 | // If the previous source was not a precise source, then we can be | |
116 | // sure that it's already been updated if we've already loaded it. | |
7a2facba | 117 | Some(&(ref previous, _)) if previous.precise().is_none() => { |
f162aed2 | 118 | debug!("load/precise {}", namespace); |
1e682848 | 119 | return Ok(()); |
6ab93117 AC |
120 | } |
121 | ||
122 | // If the previous source has the same precise version as we do, | |
123 | // then we're done, otherwise we need to need to move forward | |
124 | // updating this source. | |
125 | Some(&(ref previous, _)) => { | |
7a2facba | 126 | if previous.precise() == namespace.precise() { |
f162aed2 | 127 | debug!("load/match {}", namespace); |
1e682848 | 128 | return Ok(()); |
6ab93117 | 129 | } |
f162aed2 AC |
130 | debug!("load/mismatch {}", namespace); |
131 | } | |
132 | None => { | |
133 | debug!("load/missing {}", namespace); | |
6ab93117 | 134 | } |
6ab93117 | 135 | } |
5451f95d | 136 | |
82655b46 | 137 | self.load(namespace, kind)?; |
9b278af1 YKCL |
138 | Ok(()) |
139 | } | |
140 | ||
e5a11190 E |
141 | pub fn add_sources(&mut self, ids: impl IntoIterator<Item = SourceId>) -> CargoResult<()> { |
142 | for id in ids { | |
82655b46 | 143 | self.ensure_loaded(id, Kind::Locked)?; |
1a35097a AC |
144 | } |
145 | Ok(()) | |
146 | } | |
147 | ||
b02023ee AK |
148 | pub fn add_preloaded(&mut self, source: Box<Source + 'cfg>) { |
149 | self.add_source(source, Kind::Locked); | |
bc60f64b AC |
150 | } |
151 | ||
68c6f2b0 | 152 | fn add_source(&mut self, source: Box<Source + 'cfg>, kind: Kind) { |
e5a11190 | 153 | let id = source.source_id(); |
e3835f70 | 154 | self.sources.insert(source); |
e5a11190 | 155 | self.source_ids.insert(id, (id, kind)); |
bc60f64b AC |
156 | } |
157 | ||
b02023ee | 158 | pub fn add_override(&mut self, source: Box<Source + 'cfg>) { |
e5a11190 | 159 | self.overrides.push(source.source_id()); |
b02023ee | 160 | self.add_source(source, Kind::Override); |
e465ace4 TCS |
161 | } |
162 | ||
816373d9 | 163 | pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) { |
155dee54 AC |
164 | trace!("register_lock: {}", id); |
165 | for dep in deps.iter() { | |
166 | trace!("\t-> {}", dep); | |
167 | } | |
e5a11190 E |
168 | let sub_map = self |
169 | .locked | |
170 | .entry(id.source_id()) | |
1e682848 AC |
171 | .or_insert_with(HashMap::new); |
172 | let sub_vec = sub_map | |
173 | .entry(id.name().to_string()) | |
174 | .or_insert_with(Vec::new); | |
816373d9 AC |
175 | sub_vec.push((id, deps)); |
176 | } | |
177 | ||
eebddd37 AC |
178 | /// Insert a `[patch]` section into this registry. |
179 | /// | |
180 | /// This method will insert a `[patch]` section for the `url` specified, | |
181 | /// with the given list of dependencies. The `url` specified is the URL of | |
182 | /// the source to patch (for example this is `crates-io` in the manifest). | |
183 | /// The `deps` is an array of all the entries in the `[patch]` section of | |
184 | /// the manifest. | |
185 | /// | |
186 | /// Here the `deps` will be resolved to a precise version and stored | |
187 | /// internally for future calls to `query` below. It's expected that `deps` | |
188 | /// have had `lock_to` call already, if applicable. (e.g. if a lock file was | |
189 | /// already present). | |
190 | /// | |
191 | /// Note that the patch list specified here *will not* be available to | |
192 | /// `query` until `lock_patches` is called below, which should be called | |
193 | /// once all patches have been added. | |
61a3c68b | 194 | pub fn patch(&mut self, url: &Url, deps: &[Dependency]) -> CargoResult<()> { |
eebddd37 AC |
195 | // First up we need to actually resolve each `deps` specification to |
196 | // precisely one summary. We're not using the `query` method below as it | |
197 | // internally uses maps we're building up as part of this method | |
198 | // (`patches_available` and `patches). Instead we're going straight to | |
199 | // the source to load information from it. | |
200 | // | |
201 | // Remember that each dependency listed in `[patch]` has to resolve to | |
202 | // precisely one package, so that's why we're just creating a flat list | |
203 | // of summaries which should be the same length as `deps` above. | |
e5a11190 E |
204 | let unlocked_summaries = deps |
205 | .iter() | |
1e682848 | 206 | .map(|dep| { |
e5a11190 E |
207 | debug!( |
208 | "registring a patch for `{}` with `{}`", | |
209 | url, | |
210 | dep.package_name() | |
211 | ); | |
1e682848 AC |
212 | |
213 | // Go straight to the source for resolving `dep`. Load it as we | |
214 | // normally would and then ask it directly for the list of summaries | |
215 | // corresponding to this `dep`. | |
216 | self.ensure_loaded(dep.source_id(), Kind::Normal) | |
217 | .chain_err(|| { | |
218 | format_err!( | |
219 | "failed to load source for a dependency \ | |
220 | on `{}`", | |
5295cadd | 221 | dep.package_name() |
1e682848 AC |
222 | ) |
223 | })?; | |
224 | ||
e5a11190 E |
225 | let mut summaries = self |
226 | .sources | |
1e682848 AC |
227 | .get_mut(dep.source_id()) |
228 | .expect("loaded source not present") | |
229 | .query_vec(dep)? | |
230 | .into_iter(); | |
231 | ||
232 | let summary = match summaries.next() { | |
233 | Some(summary) => summary, | |
234 | None => bail!( | |
235 | "patch for `{}` in `{}` did not resolve to any crates. If this is \ | |
236 | unexpected, you may wish to consult: \ | |
237 | https://github.com/rust-lang/cargo/issues/4678", | |
5295cadd | 238 | dep.package_name(), |
1e682848 AC |
239 | url |
240 | ), | |
241 | }; | |
242 | if summaries.next().is_some() { | |
243 | bail!( | |
244 | "patch for `{}` in `{}` resolved to more than one candidate", | |
5295cadd | 245 | dep.package_name(), |
1e682848 AC |
246 | url |
247 | ) | |
61a3c68b | 248 | } |
1e682848 AC |
249 | if summary.package_id().source_id().url() == url { |
250 | bail!( | |
251 | "patch for `{}` in `{}` points to the same source, but \ | |
252 | patches must point to different sources", | |
5295cadd | 253 | dep.package_name(), |
1e682848 AC |
254 | url |
255 | ); | |
256 | } | |
257 | Ok(summary) | |
258 | }) | |
259 | .collect::<CargoResult<Vec<_>>>() | |
260 | .chain_err(|| format_err!("failed to resolve patches for `{}`", url))?; | |
61a3c68b | 261 | |
eebddd37 AC |
262 | // Note that we do not use `lock` here to lock summaries! That step |
263 | // happens later once `lock_patches` is invoked. In the meantime though | |
264 | // we want to fill in the `patches_available` map (later used in the | |
265 | // `lock` method) and otherwise store the unlocked summaries in | |
266 | // `patches` to get locked in a future call to `lock_patches`. | |
1e682848 AC |
267 | let ids = unlocked_summaries |
268 | .iter() | |
eebddd37 AC |
269 | .map(|s| s.package_id()) |
270 | .cloned() | |
271 | .collect(); | |
272 | self.patches_available.insert(url.clone(), ids); | |
273 | self.patches.insert(url.clone(), unlocked_summaries); | |
61a3c68b AC |
274 | |
275 | Ok(()) | |
276 | } | |
277 | ||
eebddd37 AC |
278 | /// Lock all patch summaries added via `patch`, making them available to |
279 | /// resolution via `query`. | |
280 | /// | |
281 | /// This function will internally `lock` each summary added via `patch` | |
282 | /// above now that the full set of `patch` packages are known. This'll allow | |
283 | /// us to correctly resolve overridden dependencies between patches | |
284 | /// hopefully! | |
285 | pub fn lock_patches(&mut self) { | |
286 | assert!(!self.patches_locked); | |
287 | for summaries in self.patches.values_mut() { | |
288 | for summary in summaries { | |
289 | *summary = lock(&self.locked, &self.patches_available, summary.clone()); | |
290 | } | |
291 | } | |
292 | self.patches_locked = true; | |
293 | } | |
294 | ||
61a3c68b AC |
295 | pub fn patches(&self) -> &HashMap<Url, Vec<Summary>> { |
296 | &self.patches | |
297 | } | |
298 | ||
e5a11190 | 299 | fn load(&mut self, source_id: SourceId, kind: Kind) -> CargoResult<()> { |
f2aa5b4c | 300 | (|| { |
1868998b | 301 | debug!("loading source {}", source_id); |
82655b46 | 302 | let source = self.source_config.load(source_id)?; |
b02023ee | 303 | assert_eq!(source.source_id(), source_id); |
8214bb95 | 304 | |
38d14a59 | 305 | if kind == Kind::Override { |
e5a11190 | 306 | self.overrides.push(source_id); |
f137281b | 307 | } |
b02023ee | 308 | self.add_source(source, kind); |
e465ace4 | 309 | |
18e59304 AC |
310 | // Ensure the source has fetched all necessary remote data. |
311 | let _p = profile::start(format!("updating: {}", source_id)); | |
e3835f70 | 312 | self.sources.get_mut(source_id).unwrap().update() |
1e682848 | 313 | })() |
e5a11190 | 314 | .chain_err(|| format_err!("Unable to update {}", source_id))?; |
37cffbe0 | 315 | Ok(()) |
9b278af1 | 316 | } |
46f90ba5 | 317 | |
1e682848 | 318 | fn query_overrides(&mut self, dep: &Dependency) -> CargoResult<Option<Summary>> { |
e5a11190 | 319 | for &s in self.overrides.iter() { |
e3835f70 | 320 | let src = self.sources.get_mut(s).unwrap(); |
5295cadd | 321 | let dep = Dependency::new_override(&*dep.package_name(), s); |
842c182e | 322 | let mut results = src.query_vec(&dep)?; |
23591fe5 | 323 | if !results.is_empty() { |
1e682848 | 324 | return Ok(Some(results.remove(0))); |
fc0e6426 | 325 | } |
3b77b2c7 | 326 | } |
fc0e6426 | 327 | Ok(None) |
46f90ba5 | 328 | } |
816373d9 | 329 | |
c0306a8a AC |
330 | /// This function is used to transform a summary to another locked summary |
331 | /// if possible. This is where the concept of a lockfile comes into play. | |
332 | /// | |
333 | /// If a summary points at a package id which was previously locked, then we | |
334 | /// override the summary's id itself, as well as all dependencies, to be | |
335 | /// rewritten to the locked versions. This will transform the summary's | |
336 | /// source to a precise source (listed in the locked version) as well as | |
337 | /// transforming all of the dependencies from range requirements on | |
338 | /// imprecise sources to exact requirements on precise sources. | |
339 | /// | |
340 | /// If a summary does not point at a package id which was previously locked, | |
341 | /// or if any dependencies were added and don't have a previously listed | |
342 | /// version, we still want to avoid updating as many dependencies as | |
343 | /// possible to keep the graph stable. In this case we map all of the | |
344 | /// summary's dependencies to be rewritten to a locked version wherever | |
345 | /// possible. If we're unable to map a dependency though, we just pass it on | |
346 | /// through. | |
347 | pub fn lock(&self, summary: Summary) -> Summary { | |
eebddd37 AC |
348 | assert!(self.patches_locked); |
349 | lock(&self.locked, &self.patches_available, summary) | |
816373d9 | 350 | } |
fc0e6426 | 351 | |
1e682848 AC |
352 | fn warn_bad_override( |
353 | &self, | |
354 | override_summary: &Summary, | |
355 | real_summary: &Summary, | |
356 | ) -> CargoResult<()> { | |
772e1a17 | 357 | let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>(); |
fc0e6426 AC |
358 | |
359 | let boilerplate = "\ | |
360 | This is currently allowed but is known to produce buggy behavior with spurious | |
361 | recompiles and changes to the crate graph. Path overrides unfortunately were | |
362 | never intended to support this feature, so for now this message is just a | |
363 | warning. In the future, however, this message will become a hard error. | |
364 | ||
365 | To change the dependency graph via an override it's recommended to use the | |
366 | `[replace]` feature of Cargo instead of the path override feature. This is | |
367 | documented online at the url below for more information. | |
368 | ||
54159e84 | 369 | https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#overriding-dependencies |
fc0e6426 AC |
370 | "; |
371 | ||
372 | for dep in override_summary.dependencies() { | |
772e1a17 | 373 | if let Some(i) = real_deps.iter().position(|d| dep == *d) { |
fc0e6426 | 374 | real_deps.remove(i); |
1e682848 | 375 | continue; |
fc0e6426 | 376 | } |
1e682848 AC |
377 | let msg = format!( |
378 | "\ | |
379 | path override for crate `{}` has altered the original list of\n\ | |
380 | dependencies; the dependency on `{}` was either added or\n\ | |
381 | modified to not match the previously resolved version\n\n\ | |
382 | {}", | |
383 | override_summary.package_id().name(), | |
5295cadd | 384 | dep.package_name(), |
1e682848 AC |
385 | boilerplate |
386 | ); | |
82655b46 | 387 | self.source_config.config().shell().warn(&msg)?; |
1e682848 | 388 | return Ok(()); |
fc0e6426 AC |
389 | } |
390 | ||
5295cadd | 391 | if let Some(dep) = real_deps.get(0) { |
1e682848 AC |
392 | let msg = format!( |
393 | "\ | |
fc0e6426 AC |
394 | path override for crate `{}` has altered the original list of |
395 | dependencies; the dependency on `{}` was removed\n\n | |
1e682848 AC |
396 | {}", |
397 | override_summary.package_id().name(), | |
5295cadd | 398 | dep.package_name(), |
1e682848 AC |
399 | boilerplate |
400 | ); | |
82655b46 | 401 | self.source_config.config().shell().warn(&msg)?; |
1e682848 | 402 | return Ok(()); |
fc0e6426 AC |
403 | } |
404 | ||
405 | Ok(()) | |
406 | } | |
9b278af1 YKCL |
407 | } |
408 | ||
2fe0bf83 | 409 | impl<'cfg> Registry for PackageRegistry<'cfg> { |
84cc3d8b | 410 | fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary), fuzzy: bool) -> CargoResult<()> { |
eebddd37 | 411 | assert!(self.patches_locked); |
e3835f70 AC |
412 | let (override_summary, n, to_warn) = { |
413 | // Look for an override and get ready to query the real source. | |
23591fe5 | 414 | let override_summary = self.query_overrides(dep)?; |
61a3c68b AC |
415 | |
416 | // Next up on our list of candidates is to check the `[patch]` | |
417 | // section of the manifest. Here we look through all patches | |
418 | // relevant to the source that `dep` points to, and then we match | |
419 | // name/version. Note that we don't use `dep.matches(..)` because | |
420 | // the patches, by definition, come from a different source. | |
421 | // This means that `dep.matches(..)` will always return false, when | |
422 | // what we really care about is the name/version match. | |
423 | let mut patches = Vec::<Summary>::new(); | |
424 | if let Some(extra) = self.patches.get(dep.source_id().url()) { | |
1e682848 AC |
425 | patches.extend( |
426 | extra | |
427 | .iter() | |
51d23560 | 428 | .filter(|s| dep.matches_ignoring_source(s.package_id())) |
1e682848 AC |
429 | .cloned(), |
430 | ); | |
61a3c68b AC |
431 | } |
432 | ||
433 | // A crucial feature of the `[patch]` feature is that we *don't* | |
434 | // query the actual registry if we have a "locked" dependency. A | |
435 | // locked dep basically just means a version constraint of `=a.b.c`, | |
436 | // and because patches take priority over the actual source then if | |
437 | // we have a candidate we're done. | |
438 | if patches.len() == 1 && dep.is_locked() { | |
439 | let patch = patches.remove(0); | |
440 | match override_summary { | |
441 | Some(summary) => (summary, 1, Some(patch)), | |
442 | None => { | |
443 | f(patch); | |
1e682848 | 444 | return Ok(()); |
61a3c68b AC |
445 | } |
446 | } | |
447 | } else { | |
23591fe5 | 448 | if !patches.is_empty() { |
1e682848 AC |
449 | debug!( |
450 | "found {} patches with an unlocked dep on `{}` at {} \ | |
451 | with `{}`, \ | |
452 | looking at sources", | |
453 | patches.len(), | |
5295cadd | 454 | dep.package_name(), |
1e682848 AC |
455 | dep.source_id(), |
456 | dep.version_req() | |
457 | ); | |
e3835f70 AC |
458 | } |
459 | ||
61a3c68b | 460 | // Ensure the requested source_id is loaded |
1e682848 AC |
461 | self.ensure_loaded(dep.source_id(), Kind::Normal) |
462 | .chain_err(|| { | |
463 | format_err!( | |
464 | "failed to load source for a dependency \ | |
465 | on `{}`", | |
5295cadd | 466 | dep.package_name() |
1e682848 AC |
467 | ) |
468 | })?; | |
61a3c68b AC |
469 | |
470 | let source = self.sources.get_mut(dep.source_id()); | |
471 | match (override_summary, source) { | |
472 | (Some(_), None) => bail!("override found but no real ones"), | |
473 | (None, None) => return Ok(()), | |
474 | ||
475 | // If we don't have an override then we just ship | |
476 | // everything upstairs after locking the summary | |
477 | (None, Some(source)) => { | |
478 | for patch in patches.iter() { | |
479 | f(patch.clone()); | |
480 | } | |
481 | ||
482 | // Our sources shouldn't ever come back to us with two | |
483 | // summaries that have the same version. We could, | |
484 | // however, have an `[patch]` section which is in use | |
485 | // to override a version in the registry. This means | |
486 | // that if our `summary` in this loop has the same | |
487 | // version as something in `patches` that we've | |
488 | // already selected, then we skip this `summary`. | |
489 | let locked = &self.locked; | |
eebddd37 | 490 | let all_patches = &self.patches_available; |
84cc3d8b | 491 | let callback = &mut |summary: Summary| { |
61a3c68b AC |
492 | for patch in patches.iter() { |
493 | let patch = patch.package_id().version(); | |
494 | if summary.package_id().version() == patch { | |
1e682848 | 495 | return; |
61a3c68b AC |
496 | } |
497 | } | |
498 | f(lock(locked, all_patches, summary)) | |
84cc3d8b E |
499 | }; |
500 | return if fuzzy { | |
501 | source.fuzzy_query(dep, callback) | |
502 | } else { | |
503 | source.query(dep, callback) | |
504 | }; | |
61a3c68b AC |
505 | } |
506 | ||
507 | // If we have an override summary then we query the source | |
508 | // to sanity check its results. We don't actually use any of | |
509 | // the summaries it gives us though. | |
510 | (Some(override_summary), Some(source)) => { | |
23591fe5 | 511 | if !patches.is_empty() { |
61a3c68b AC |
512 | bail!("found patches and a path override") |
513 | } | |
514 | let mut n = 0; | |
515 | let mut to_warn = None; | |
84cc3d8b E |
516 | { |
517 | let callback = &mut |summary| { | |
518 | n += 1; | |
519 | to_warn = Some(summary); | |
520 | }; | |
521 | if fuzzy { | |
522 | source.fuzzy_query(dep, callback)?; | |
523 | } else { | |
524 | source.query(dep, callback)?; | |
525 | } | |
526 | } | |
61a3c68b AC |
527 | (override_summary, n, to_warn) |
528 | } | |
e3835f70 | 529 | } |
86774bd8 | 530 | } |
816373d9 AC |
531 | }; |
532 | ||
842c182e AC |
533 | if n > 1 { |
534 | bail!("found an override with a non-locked list"); | |
535 | } else if let Some(summary) = to_warn { | |
536 | self.warn_bad_override(&override_summary, &summary)?; | |
537 | } | |
538 | f(self.lock(override_summary)); | |
539 | Ok(()) | |
8f6d4afb | 540 | } |
20cfb41e | 541 | |
e5a11190 | 542 | fn describe_source(&self, id: SourceId) -> String { |
20cfb41e AC |
543 | match self.sources.get(id) { |
544 | Some(src) => src.describe(), | |
545 | None => id.to_string(), | |
546 | } | |
547 | } | |
548 | ||
e5a11190 | 549 | fn is_replaced(&self, id: SourceId) -> bool { |
20cfb41e AC |
550 | match self.sources.get(id) { |
551 | Some(src) => src.is_replaced(), | |
552 | None => false, | |
553 | } | |
554 | } | |
f4d6d021 | 555 | } |
e465ace4 | 556 | |
1e682848 AC |
557 | fn lock(locked: &LockedMap, patches: &HashMap<Url, Vec<PackageId>>, summary: Summary) -> Summary { |
558 | let pair = locked | |
e5a11190 | 559 | .get(&summary.source_id()) |
1e682848 AC |
560 | .and_then(|map| map.get(&*summary.name())) |
561 | .and_then(|vec| vec.iter().find(|&&(ref id, _)| id == summary.package_id())); | |
e3835f70 AC |
562 | |
563 | trace!("locking summary of {}", summary.package_id()); | |
564 | ||
565 | // Lock the summary's id if possible | |
566 | let summary = match pair { | |
567 | Some(&(ref precise, _)) => summary.override_id(precise.clone()), | |
568 | None => summary, | |
569 | }; | |
61a3c68b | 570 | summary.map_dependencies(|dep| { |
e5a11190 E |
571 | trace!( |
572 | "\t{}/{}/{}", | |
573 | dep.package_name(), | |
574 | dep.version_req(), | |
575 | dep.source_id() | |
576 | ); | |
e3835f70 AC |
577 | |
578 | // If we've got a known set of overrides for this summary, then | |
579 | // one of a few cases can arise: | |
580 | // | |
581 | // 1. We have a lock entry for this dependency from the same | |
582 | // source as it's listed as coming from. In this case we make | |
583 | // sure to lock to precisely the given package id. | |
584 | // | |
585 | // 2. We have a lock entry for this dependency, but it's from a | |
586 | // different source than what's listed, or the version | |
587 | // requirement has changed. In this case we must discard the | |
588 | // locked version because the dependency needs to be | |
589 | // re-resolved. | |
590 | // | |
591 | // 3. We don't have a lock entry for this dependency, in which | |
592 | // case it was likely an optional dependency which wasn't | |
593 | // included previously so we just pass it through anyway. | |
594 | // | |
595 | // Cases 1/2 are handled by `matches_id` and case 3 is handled by | |
596 | // falling through to the logic below. | |
597 | if let Some(&(_, ref locked_deps)) = pair { | |
598 | let locked = locked_deps.iter().find(|id| dep.matches_id(id)); | |
599 | if let Some(locked) = locked { | |
600 | trace!("\tfirst hit on {}", locked); | |
61a3c68b | 601 | let mut dep = dep.clone(); |
e3835f70 | 602 | dep.lock_to(locked); |
1e682848 | 603 | return dep; |
e3835f70 AC |
604 | } |
605 | } | |
606 | ||
607 | // If this dependency did not have a locked version, then we query | |
608 | // all known locked packages to see if they match this dependency. | |
609 | // If anything does then we lock it to that and move on. | |
1e682848 | 610 | let v = locked |
e5a11190 | 611 | .get(&dep.source_id()) |
5295cadd | 612 | .and_then(|map| map.get(&*dep.package_name())) |
1e682848 | 613 | .and_then(|vec| vec.iter().find(|&&(ref id, _)| dep.matches_id(id))); |
61a3c68b AC |
614 | if let Some(&(ref id, _)) = v { |
615 | trace!("\tsecond hit on {}", id); | |
616 | let mut dep = dep.clone(); | |
617 | dep.lock_to(id); | |
1e682848 | 618 | return dep; |
61a3c68b AC |
619 | } |
620 | ||
621 | // Finally we check to see if any registered patches correspond to | |
622 | // this dependency. | |
623 | let v = patches.get(dep.source_id().url()).map(|vec| { | |
624 | let dep2 = dep.clone(); | |
e5a11190 | 625 | let mut iter = vec.iter().filter(move |p| dep2.matches_ignoring_source(p)); |
61a3c68b AC |
626 | (iter.next(), iter) |
627 | }); | |
eebddd37 | 628 | if let Some((Some(patch_id), mut remaining)) = v { |
61a3c68b | 629 | assert!(remaining.next().is_none()); |
eebddd37 | 630 | let patch_source = patch_id.source_id(); |
1e682848 | 631 | let patch_locked = locked |
e5a11190 | 632 | .get(&patch_source) |
1e682848 AC |
633 | .and_then(|m| m.get(&*patch_id.name())) |
634 | .map(|list| list.iter().any(|&(ref id, _)| id == patch_id)) | |
635 | .unwrap_or(false); | |
61a3c68b AC |
636 | |
637 | if patch_locked { | |
eebddd37 AC |
638 | trace!("\tthird hit on {}", patch_id); |
639 | let req = VersionReq::exact(patch_id.version()); | |
61a3c68b AC |
640 | let mut dep = dep.clone(); |
641 | dep.set_version_req(req); | |
1e682848 | 642 | return dep; |
e3835f70 | 643 | } |
e3835f70 | 644 | } |
61a3c68b AC |
645 | |
646 | trace!("\tnope, unlocked"); | |
23591fe5 | 647 | dep |
e3835f70 AC |
648 | }) |
649 | } |