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