]>
Commit | Line | Data |
---|---|---|
44005146 | 1 | use std::collections::{HashMap, HashSet}; |
7192e29a | 2 | |
7f73a6c7 | 3 | use crate::core::PackageSet; |
04ddd4d0 DW |
4 | use crate::core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary}; |
5 | use crate::sources::config::SourceConfigMap; | |
ebca5190 | 6 | use crate::util::errors::CargoResult; |
7f73a6c7 | 7 | use crate::util::interning::InternedString; |
e5454122 | 8 | use crate::util::{profile, CanonicalUrl, Config}; |
ebca5190 | 9 | use anyhow::{bail, Context as _}; |
07162dba AC |
10 | use log::{debug, trace}; |
11 | use semver::VersionReq; | |
12 | use url::Url; | |
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`. | |
8418a3ae E |
94 | // The next level is keyed by the name of the package... |
95 | (SourceId, InternedString), | |
96 | // ... and the value here is a list of tuples. The first element of each | |
97 | // tuple is a package which has the source/name used to get to this | |
98 | // point. The second element of each tuple is the list of locked | |
99 | // dependencies that the first element has. | |
100 | Vec<(PackageId, Vec<PackageId>)>, | |
803bf329 | 101 | >; |
e3835f70 | 102 | |
abe56727 | 103 | #[derive(PartialEq, Eq, Clone, Copy)] |
6ab93117 AC |
104 | enum Kind { |
105 | Override, | |
106 | Locked, | |
107 | Normal, | |
108 | } | |
109 | ||
2fe0bf83 | 110 | impl<'cfg> PackageRegistry<'cfg> { |
8214bb95 | 111 | pub fn new(config: &'cfg Config) -> CargoResult<PackageRegistry<'cfg>> { |
82655b46 | 112 | let source_config = SourceConfigMap::new(config)?; |
8214bb95 | 113 | Ok(PackageRegistry { |
c94804bd | 114 | config, |
e3835f70 | 115 | sources: SourceMap::new(), |
6ab93117 | 116 | source_ids: HashMap::new(), |
54d738b0 | 117 | overrides: Vec::new(), |
0247dc42 | 118 | source_config, |
816373d9 | 119 | locked: HashMap::new(), |
44005146 | 120 | yanked_whitelist: HashSet::new(), |
61a3c68b | 121 | patches: HashMap::new(), |
eebddd37 AC |
122 | patches_locked: false, |
123 | patches_available: HashMap::new(), | |
8214bb95 | 124 | }) |
98322afd YKCL |
125 | } |
126 | ||
468f243e | 127 | pub fn get(self, package_ids: &[PackageId]) -> CargoResult<PackageSet<'cfg>> { |
e3835f70 | 128 | trace!("getting packages; sources={}", self.sources.len()); |
c94804bd | 129 | PackageSet::new(package_ids, self.sources, self.config) |
5451f95d CL |
130 | } |
131 | ||
e5a11190 E |
132 | fn ensure_loaded(&mut self, namespace: SourceId, kind: Kind) -> CargoResult<()> { |
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. | |
07162dba | 137 | Some((_, Kind::Locked)) => { |
f162aed2 | 138 | debug!("load/locked {}", namespace); |
1e682848 | 139 | return Ok(()); |
f162aed2 | 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. | |
07162dba | 144 | Some((previous, _)) if previous.precise().is_none() => { |
f162aed2 | 145 | debug!("load/precise {}", namespace); |
1e682848 | 146 | return Ok(()); |
6ab93117 AC |
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. | |
07162dba | 152 | Some((previous, _)) => { |
7a2facba | 153 | if previous.precise() == namespace.precise() { |
f162aed2 | 154 | debug!("load/match {}", namespace); |
1e682848 | 155 | return Ok(()); |
6ab93117 | 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 | ||
e5a11190 E |
168 | pub fn add_sources(&mut self, ids: impl IntoIterator<Item = SourceId>) -> CargoResult<()> { |
169 | for id in ids { | |
82655b46 | 170 | self.ensure_loaded(id, Kind::Locked)?; |
1a35097a AC |
171 | } |
172 | Ok(()) | |
173 | } | |
174 | ||
b8b7faee | 175 | pub fn add_preloaded(&mut self, source: Box<dyn Source + 'cfg>) { |
b02023ee | 176 | self.add_source(source, Kind::Locked); |
bc60f64b AC |
177 | } |
178 | ||
b8b7faee | 179 | fn add_source(&mut self, source: Box<dyn Source + 'cfg>, kind: Kind) { |
e5a11190 | 180 | let id = source.source_id(); |
e3835f70 | 181 | self.sources.insert(source); |
e5a11190 | 182 | self.source_ids.insert(id, (id, kind)); |
bc60f64b AC |
183 | } |
184 | ||
b8b7faee | 185 | pub fn add_override(&mut self, source: Box<dyn Source + 'cfg>) { |
e5a11190 | 186 | self.overrides.push(source.source_id()); |
b02023ee | 187 | self.add_source(source, Kind::Override); |
e465ace4 TCS |
188 | } |
189 | ||
2f7eb42f | 190 | pub fn add_to_yanked_whitelist(&mut self, iter: impl Iterator<Item = PackageId>) { |
4a2f810d AC |
191 | let pkgs = iter.collect::<Vec<_>>(); |
192 | for (_, source) in self.sources.sources_mut() { | |
193 | source.add_to_yanked_whitelist(&pkgs); | |
194 | } | |
195 | self.yanked_whitelist.extend(pkgs); | |
2f7eb42f E |
196 | } |
197 | ||
61e59278 E |
198 | /// remove all residual state from previous lock files. |
199 | pub fn clear_lock(&mut self) { | |
200 | trace!("clear_lock"); | |
201 | self.locked = HashMap::new(); | |
202 | } | |
203 | ||
816373d9 | 204 | pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) { |
155dee54 AC |
205 | trace!("register_lock: {}", id); |
206 | for dep in deps.iter() { | |
207 | trace!("\t-> {}", dep); | |
208 | } | |
8418a3ae | 209 | let sub_vec = self |
e5a11190 | 210 | .locked |
8418a3ae E |
211 | .entry((id.source_id(), id.name())) |
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 | |
b78cb373 EH |
225 | /// internally for future calls to `query` below. `deps` should be a tuple |
226 | /// where the first element is the patch definition straight from the | |
227 | /// manifest, and the second element is an optional variant where the | |
228 | /// patch has been locked. This locked patch is the patch locked to | |
229 | /// a specific version found in Cargo.lock. This will be `None` if | |
230 | /// `Cargo.lock` doesn't exist, or the patch did not match any existing | |
231 | /// entries in `Cargo.lock`. | |
eebddd37 AC |
232 | /// |
233 | /// Note that the patch list specified here *will not* be available to | |
234 | /// `query` until `lock_patches` is called below, which should be called | |
235 | /// once all patches have been added. | |
afa3aced EH |
236 | /// |
237 | /// The return value is a `Vec` of patches that should *not* be locked. | |
238 | /// This happens when the patch is locked, but the patch has been updated | |
239 | /// so the locked value is no longer correct. | |
b78cb373 EH |
240 | pub fn patch( |
241 | &mut self, | |
242 | url: &Url, | |
afa3aced EH |
243 | deps: &[(&Dependency, Option<(Dependency, PackageId)>)], |
244 | ) -> CargoResult<Vec<(Dependency, PackageId)>> { | |
245 | // NOTE: None of this code is aware of required features. If a patch | |
246 | // is missing a required feature, you end up with an "unused patch" | |
247 | // warning, which is very hard to understand. Ideally the warning | |
248 | // would be tailored to indicate *why* it is unused. | |
e5454122 AC |
249 | let canonical = CanonicalUrl::new(url)?; |
250 | ||
afa3aced EH |
251 | // Return value of patches that shouldn't be locked. |
252 | let mut unlock_patches = Vec::new(); | |
253 | ||
eebddd37 AC |
254 | // First up we need to actually resolve each `deps` specification to |
255 | // precisely one summary. We're not using the `query` method below as it | |
256 | // internally uses maps we're building up as part of this method | |
257 | // (`patches_available` and `patches). Instead we're going straight to | |
258 | // the source to load information from it. | |
259 | // | |
260 | // Remember that each dependency listed in `[patch]` has to resolve to | |
261 | // precisely one package, so that's why we're just creating a flat list | |
262 | // of summaries which should be the same length as `deps` above. | |
e5a11190 E |
263 | let unlocked_summaries = deps |
264 | .iter() | |
afa3aced EH |
265 | .map(|(orig_patch, locked)| { |
266 | // Remove double reference in orig_patch. Is there maybe a | |
267 | // magic pattern that could avoid this? | |
268 | let orig_patch = *orig_patch; | |
269 | // Use the locked patch if it exists, otherwise use the original. | |
270 | let dep = match locked { | |
271 | Some((locked_patch, _locked_id)) => locked_patch, | |
272 | None => orig_patch, | |
273 | }; | |
e5a11190 | 274 | debug!( |
b4cd6095 | 275 | "registering a patch for `{}` with `{}`", |
e5a11190 E |
276 | url, |
277 | dep.package_name() | |
278 | ); | |
1e682848 AC |
279 | |
280 | // Go straight to the source for resolving `dep`. Load it as we | |
281 | // normally would and then ask it directly for the list of summaries | |
282 | // corresponding to this `dep`. | |
283 | self.ensure_loaded(dep.source_id(), Kind::Normal) | |
ebca5190 | 284 | .with_context(|| { |
9099b491 | 285 | format!( |
a07fec1b | 286 | "failed to load source for dependency `{}`", |
5295cadd | 287 | dep.package_name() |
1e682848 AC |
288 | ) |
289 | })?; | |
290 | ||
b78cb373 | 291 | let source = self |
e5a11190 | 292 | .sources |
1e682848 | 293 | .get_mut(dep.source_id()) |
b78cb373 EH |
294 | .expect("loaded source not present"); |
295 | let summaries = source.query_vec(dep)?; | |
ebca5190 WL |
296 | let (summary, should_unlock) = summary_for_patch( |
297 | orig_patch, locked, summaries, source, | |
298 | ) | |
299 | .with_context(|| { | |
300 | format!( | |
301 | "patch for `{}` in `{}` failed to resolve", | |
302 | orig_patch.package_name(), | |
303 | url, | |
304 | ) | |
305 | })?; | |
afa3aced EH |
306 | debug!( |
307 | "patch summary is {:?} should_unlock={:?}", | |
308 | summary, should_unlock | |
309 | ); | |
310 | if let Some(unlock_id) = should_unlock { | |
311 | unlock_patches.push((orig_patch.clone(), unlock_id)); | |
312 | } | |
b78cb373 | 313 | |
e5454122 | 314 | if *summary.package_id().source_id().canonical_url() == canonical { |
3a18c89a | 315 | anyhow::bail!( |
1e682848 AC |
316 | "patch for `{}` in `{}` points to the same source, but \ |
317 | patches must point to different sources", | |
5295cadd | 318 | dep.package_name(), |
1e682848 AC |
319 | url |
320 | ); | |
321 | } | |
322 | Ok(summary) | |
323 | }) | |
324 | .collect::<CargoResult<Vec<_>>>() | |
9099b491 | 325 | .with_context(|| format!("failed to resolve patches for `{}`", url))?; |
61a3c68b | 326 | |
803bf329 AC |
327 | let mut name_and_version = HashSet::new(); |
328 | for summary in unlocked_summaries.iter() { | |
329 | let name = summary.package_id().name(); | |
330 | let version = summary.package_id().version(); | |
331 | if !name_and_version.insert((name, version)) { | |
332 | bail!( | |
333 | "cannot have two `[patch]` entries which both resolve \ | |
334 | to `{} v{}`", | |
335 | name, | |
336 | version | |
337 | ); | |
338 | } | |
339 | } | |
340 | ||
eebddd37 AC |
341 | // Note that we do not use `lock` here to lock summaries! That step |
342 | // happens later once `lock_patches` is invoked. In the meantime though | |
343 | // we want to fill in the `patches_available` map (later used in the | |
344 | // `lock` method) and otherwise store the unlocked summaries in | |
345 | // `patches` to get locked in a future call to `lock_patches`. | |
dae87a26 | 346 | let ids = unlocked_summaries.iter().map(|s| s.package_id()).collect(); |
e5454122 AC |
347 | self.patches_available.insert(canonical.clone(), ids); |
348 | self.patches.insert(canonical, unlocked_summaries); | |
61a3c68b | 349 | |
afa3aced | 350 | Ok(unlock_patches) |
61a3c68b AC |
351 | } |
352 | ||
eebddd37 AC |
353 | /// Lock all patch summaries added via `patch`, making them available to |
354 | /// resolution via `query`. | |
355 | /// | |
356 | /// This function will internally `lock` each summary added via `patch` | |
357 | /// above now that the full set of `patch` packages are known. This'll allow | |
358 | /// us to correctly resolve overridden dependencies between patches | |
359 | /// hopefully! | |
360 | pub fn lock_patches(&mut self) { | |
361 | assert!(!self.patches_locked); | |
362 | for summaries in self.patches.values_mut() { | |
363 | for summary in summaries { | |
afa3aced | 364 | debug!("locking patch {:?}", summary); |
eebddd37 AC |
365 | *summary = lock(&self.locked, &self.patches_available, summary.clone()); |
366 | } | |
367 | } | |
368 | self.patches_locked = true; | |
369 | } | |
370 | ||
e5454122 AC |
371 | pub fn patches(&self) -> Vec<Summary> { |
372 | self.patches | |
373 | .values() | |
374 | .flat_map(|v| v.iter().cloned()) | |
375 | .collect() | |
61a3c68b AC |
376 | } |
377 | ||
e5a11190 | 378 | fn load(&mut self, source_id: SourceId, kind: Kind) -> CargoResult<()> { |
f2aa5b4c | 379 | (|| { |
1868998b | 380 | debug!("loading source {}", source_id); |
192073bf | 381 | let source = self.source_config.load(source_id, &self.yanked_whitelist)?; |
b02023ee | 382 | assert_eq!(source.source_id(), source_id); |
8214bb95 | 383 | |
38d14a59 | 384 | if kind == Kind::Override { |
e5a11190 | 385 | self.overrides.push(source_id); |
f137281b | 386 | } |
b02023ee | 387 | self.add_source(source, kind); |
e465ace4 | 388 | |
18e59304 AC |
389 | // Ensure the source has fetched all necessary remote data. |
390 | let _p = profile::start(format!("updating: {}", source_id)); | |
e3835f70 | 391 | self.sources.get_mut(source_id).unwrap().update() |
1e682848 | 392 | })() |
9099b491 | 393 | .with_context(|| format!("Unable to update {}", source_id))?; |
37cffbe0 | 394 | Ok(()) |
9b278af1 | 395 | } |
46f90ba5 | 396 | |
1e682848 | 397 | fn query_overrides(&mut self, dep: &Dependency) -> CargoResult<Option<Summary>> { |
e5a11190 | 398 | for &s in self.overrides.iter() { |
e3835f70 | 399 | let src = self.sources.get_mut(s).unwrap(); |
c14bb6e0 | 400 | let dep = Dependency::new_override(dep.package_name(), s); |
842c182e | 401 | let mut results = src.query_vec(&dep)?; |
23591fe5 | 402 | if !results.is_empty() { |
1e682848 | 403 | return Ok(Some(results.remove(0))); |
fc0e6426 | 404 | } |
3b77b2c7 | 405 | } |
fc0e6426 | 406 | Ok(None) |
46f90ba5 | 407 | } |
816373d9 | 408 | |
c0306a8a | 409 | /// This function is used to transform a summary to another locked summary |
f7c91ba6 | 410 | /// if possible. This is where the concept of a lock file comes into play. |
c0306a8a | 411 | /// |
f7c91ba6 AR |
412 | /// If a summary points at a package ID which was previously locked, then we |
413 | /// override the summary's ID itself, as well as all dependencies, to be | |
c0306a8a AC |
414 | /// rewritten to the locked versions. This will transform the summary's |
415 | /// source to a precise source (listed in the locked version) as well as | |
416 | /// transforming all of the dependencies from range requirements on | |
417 | /// imprecise sources to exact requirements on precise sources. | |
418 | /// | |
f7c91ba6 | 419 | /// If a summary does not point at a package ID which was previously locked, |
c0306a8a AC |
420 | /// or if any dependencies were added and don't have a previously listed |
421 | /// version, we still want to avoid updating as many dependencies as | |
422 | /// possible to keep the graph stable. In this case we map all of the | |
423 | /// summary's dependencies to be rewritten to a locked version wherever | |
424 | /// possible. If we're unable to map a dependency though, we just pass it on | |
425 | /// through. | |
426 | pub fn lock(&self, summary: Summary) -> Summary { | |
eebddd37 AC |
427 | assert!(self.patches_locked); |
428 | lock(&self.locked, &self.patches_available, summary) | |
816373d9 | 429 | } |
fc0e6426 | 430 | |
1e682848 AC |
431 | fn warn_bad_override( |
432 | &self, | |
433 | override_summary: &Summary, | |
434 | real_summary: &Summary, | |
435 | ) -> CargoResult<()> { | |
772e1a17 | 436 | let mut real_deps = real_summary.dependencies().iter().collect::<Vec<_>>(); |
fc0e6426 AC |
437 | |
438 | let boilerplate = "\ | |
439 | This is currently allowed but is known to produce buggy behavior with spurious | |
440 | recompiles and changes to the crate graph. Path overrides unfortunately were | |
441 | never intended to support this feature, so for now this message is just a | |
442 | warning. In the future, however, this message will become a hard error. | |
443 | ||
444 | To change the dependency graph via an override it's recommended to use the | |
445 | `[replace]` feature of Cargo instead of the path override feature. This is | |
446 | documented online at the url below for more information. | |
447 | ||
22db875b | 448 | https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html |
fc0e6426 AC |
449 | "; |
450 | ||
451 | for dep in override_summary.dependencies() { | |
772e1a17 | 452 | if let Some(i) = real_deps.iter().position(|d| dep == *d) { |
fc0e6426 | 453 | real_deps.remove(i); |
1e682848 | 454 | continue; |
fc0e6426 | 455 | } |
1e682848 | 456 | let msg = format!( |
a4e96114 | 457 | "path override for crate `{}` has altered the original list of\n\ |
1e682848 AC |
458 | dependencies; the dependency on `{}` was either added or\n\ |
459 | modified to not match the previously resolved version\n\n\ | |
460 | {}", | |
461 | override_summary.package_id().name(), | |
5295cadd | 462 | dep.package_name(), |
1e682848 AC |
463 | boilerplate |
464 | ); | |
82655b46 | 465 | self.source_config.config().shell().warn(&msg)?; |
1e682848 | 466 | return Ok(()); |
fc0e6426 AC |
467 | } |
468 | ||
5295cadd | 469 | if let Some(dep) = real_deps.get(0) { |
1e682848 | 470 | let msg = format!( |
a4e96114 EH |
471 | "path override for crate `{}` has altered the original list of\n\ |
472 | dependencies; the dependency on `{}` was removed\n\n\ | |
473 | {}", | |
1e682848 | 474 | override_summary.package_id().name(), |
5295cadd | 475 | dep.package_name(), |
1e682848 AC |
476 | boilerplate |
477 | ); | |
82655b46 | 478 | self.source_config.config().shell().warn(&msg)?; |
1e682848 | 479 | return Ok(()); |
fc0e6426 AC |
480 | } |
481 | ||
482 | Ok(()) | |
483 | } | |
9b278af1 YKCL |
484 | } |
485 | ||
2fe0bf83 | 486 | impl<'cfg> Registry for PackageRegistry<'cfg> { |
76ce4dfe AC |
487 | fn query( |
488 | &mut self, | |
489 | dep: &Dependency, | |
490 | f: &mut dyn FnMut(Summary), | |
491 | fuzzy: bool, | |
492 | ) -> CargoResult<()> { | |
eebddd37 | 493 | assert!(self.patches_locked); |
e3835f70 AC |
494 | let (override_summary, n, to_warn) = { |
495 | // Look for an override and get ready to query the real source. | |
23591fe5 | 496 | let override_summary = self.query_overrides(dep)?; |
61a3c68b AC |
497 | |
498 | // Next up on our list of candidates is to check the `[patch]` | |
499 | // section of the manifest. Here we look through all patches | |
500 | // relevant to the source that `dep` points to, and then we match | |
501 | // name/version. Note that we don't use `dep.matches(..)` because | |
502 | // the patches, by definition, come from a different source. | |
503 | // This means that `dep.matches(..)` will always return false, when | |
504 | // what we really care about is the name/version match. | |
505 | let mut patches = Vec::<Summary>::new(); | |
e5454122 | 506 | if let Some(extra) = self.patches.get(dep.source_id().canonical_url()) { |
1e682848 AC |
507 | patches.extend( |
508 | extra | |
509 | .iter() | |
51d23560 | 510 | .filter(|s| dep.matches_ignoring_source(s.package_id())) |
1e682848 AC |
511 | .cloned(), |
512 | ); | |
61a3c68b AC |
513 | } |
514 | ||
515 | // A crucial feature of the `[patch]` feature is that we *don't* | |
516 | // query the actual registry if we have a "locked" dependency. A | |
517 | // locked dep basically just means a version constraint of `=a.b.c`, | |
518 | // and because patches take priority over the actual source then if | |
519 | // we have a candidate we're done. | |
520 | if patches.len() == 1 && dep.is_locked() { | |
521 | let patch = patches.remove(0); | |
522 | match override_summary { | |
523 | Some(summary) => (summary, 1, Some(patch)), | |
524 | None => { | |
525 | f(patch); | |
1e682848 | 526 | return Ok(()); |
61a3c68b AC |
527 | } |
528 | } | |
529 | } else { | |
23591fe5 | 530 | if !patches.is_empty() { |
1e682848 AC |
531 | debug!( |
532 | "found {} patches with an unlocked dep on `{}` at {} \ | |
533 | with `{}`, \ | |
534 | looking at sources", | |
535 | patches.len(), | |
5295cadd | 536 | dep.package_name(), |
1e682848 AC |
537 | dep.source_id(), |
538 | dep.version_req() | |
539 | ); | |
e3835f70 AC |
540 | } |
541 | ||
61a3c68b | 542 | // Ensure the requested source_id is loaded |
1e682848 | 543 | self.ensure_loaded(dep.source_id(), Kind::Normal) |
ebca5190 | 544 | .with_context(|| { |
9099b491 | 545 | format!( |
a07fec1b | 546 | "failed to load source for dependency `{}`", |
5295cadd | 547 | dep.package_name() |
1e682848 AC |
548 | ) |
549 | })?; | |
61a3c68b AC |
550 | |
551 | let source = self.sources.get_mut(dep.source_id()); | |
552 | match (override_summary, source) { | |
3a18c89a | 553 | (Some(_), None) => anyhow::bail!("override found but no real ones"), |
61a3c68b AC |
554 | (None, None) => return Ok(()), |
555 | ||
556 | // If we don't have an override then we just ship | |
557 | // everything upstairs after locking the summary | |
558 | (None, Some(source)) => { | |
559 | for patch in patches.iter() { | |
560 | f(patch.clone()); | |
561 | } | |
562 | ||
563 | // Our sources shouldn't ever come back to us with two | |
564 | // summaries that have the same version. We could, | |
565 | // however, have an `[patch]` section which is in use | |
566 | // to override a version in the registry. This means | |
567 | // that if our `summary` in this loop has the same | |
568 | // version as something in `patches` that we've | |
569 | // already selected, then we skip this `summary`. | |
570 | let locked = &self.locked; | |
eebddd37 | 571 | let all_patches = &self.patches_available; |
84cc3d8b | 572 | let callback = &mut |summary: Summary| { |
61a3c68b AC |
573 | for patch in patches.iter() { |
574 | let patch = patch.package_id().version(); | |
575 | if summary.package_id().version() == patch { | |
1e682848 | 576 | return; |
61a3c68b AC |
577 | } |
578 | } | |
579 | f(lock(locked, all_patches, summary)) | |
84cc3d8b E |
580 | }; |
581 | return if fuzzy { | |
582 | source.fuzzy_query(dep, callback) | |
583 | } else { | |
584 | source.query(dep, callback) | |
585 | }; | |
61a3c68b AC |
586 | } |
587 | ||
588 | // If we have an override summary then we query the source | |
589 | // to sanity check its results. We don't actually use any of | |
590 | // the summaries it gives us though. | |
591 | (Some(override_summary), Some(source)) => { | |
23591fe5 | 592 | if !patches.is_empty() { |
3a18c89a | 593 | anyhow::bail!("found patches and a path override") |
61a3c68b AC |
594 | } |
595 | let mut n = 0; | |
596 | let mut to_warn = None; | |
84cc3d8b E |
597 | { |
598 | let callback = &mut |summary| { | |
599 | n += 1; | |
600 | to_warn = Some(summary); | |
601 | }; | |
602 | if fuzzy { | |
603 | source.fuzzy_query(dep, callback)?; | |
604 | } else { | |
605 | source.query(dep, callback)?; | |
606 | } | |
607 | } | |
61a3c68b AC |
608 | (override_summary, n, to_warn) |
609 | } | |
e3835f70 | 610 | } |
86774bd8 | 611 | } |
816373d9 AC |
612 | }; |
613 | ||
842c182e | 614 | if n > 1 { |
3a18c89a | 615 | anyhow::bail!("found an override with a non-locked list"); |
842c182e AC |
616 | } else if let Some(summary) = to_warn { |
617 | self.warn_bad_override(&override_summary, &summary)?; | |
618 | } | |
619 | f(self.lock(override_summary)); | |
620 | Ok(()) | |
8f6d4afb | 621 | } |
20cfb41e | 622 | |
e5a11190 | 623 | fn describe_source(&self, id: SourceId) -> String { |
20cfb41e AC |
624 | match self.sources.get(id) { |
625 | Some(src) => src.describe(), | |
626 | None => id.to_string(), | |
627 | } | |
628 | } | |
629 | ||
e5a11190 | 630 | fn is_replaced(&self, id: SourceId) -> bool { |
20cfb41e AC |
631 | match self.sources.get(id) { |
632 | Some(src) => src.is_replaced(), | |
633 | None => false, | |
634 | } | |
635 | } | |
f4d6d021 | 636 | } |
e465ace4 | 637 | |
e5454122 AC |
638 | fn lock( |
639 | locked: &LockedMap, | |
640 | patches: &HashMap<CanonicalUrl, Vec<PackageId>>, | |
641 | summary: Summary, | |
642 | ) -> Summary { | |
1e682848 | 643 | let pair = locked |
8418a3ae | 644 | .get(&(summary.source_id(), summary.name())) |
dae87a26 | 645 | .and_then(|vec| vec.iter().find(|&&(id, _)| id == summary.package_id())); |
e3835f70 AC |
646 | |
647 | trace!("locking summary of {}", summary.package_id()); | |
648 | ||
f7c91ba6 | 649 | // Lock the summary's ID if possible |
e3835f70 | 650 | let summary = match pair { |
6694fdb6 | 651 | Some((precise, _)) => summary.override_id(*precise), |
e3835f70 AC |
652 | None => summary, |
653 | }; | |
61a3c68b | 654 | summary.map_dependencies(|dep| { |
e5a11190 E |
655 | trace!( |
656 | "\t{}/{}/{}", | |
657 | dep.package_name(), | |
658 | dep.version_req(), | |
659 | dep.source_id() | |
660 | ); | |
e3835f70 AC |
661 | |
662 | // If we've got a known set of overrides for this summary, then | |
663 | // one of a few cases can arise: | |
664 | // | |
665 | // 1. We have a lock entry for this dependency from the same | |
666 | // source as it's listed as coming from. In this case we make | |
f7c91ba6 | 667 | // sure to lock to precisely the given package ID. |
e3835f70 AC |
668 | // |
669 | // 2. We have a lock entry for this dependency, but it's from a | |
670 | // different source than what's listed, or the version | |
671 | // requirement has changed. In this case we must discard the | |
672 | // locked version because the dependency needs to be | |
673 | // re-resolved. | |
674 | // | |
803bf329 AC |
675 | // 3. We have a lock entry for this dependency, but it's from a |
676 | // different source than what's listed. This lock though happens | |
677 | // through `[patch]`, so we want to preserve it. | |
678 | // | |
679 | // 4. We don't have a lock entry for this dependency, in which | |
e3835f70 AC |
680 | // case it was likely an optional dependency which wasn't |
681 | // included previously so we just pass it through anyway. | |
682 | // | |
803bf329 AC |
683 | // Cases 1/2 are handled by `matches_id`, case 3 is handled specially, |
684 | // and case 4 is handled by falling through to the logic below. | |
685 | if let Some((_, locked_deps)) = pair { | |
686 | let locked = locked_deps.iter().find(|&&id| { | |
687 | // If the dependency matches the package id exactly then we've | |
688 | // found a match, this is the id the dependency was previously | |
689 | // locked to. | |
690 | if dep.matches_id(id) { | |
691 | return true; | |
692 | } | |
693 | ||
694 | // If the name/version doesn't match, then we definitely don't | |
695 | // have a match whatsoever. Otherwise we need to check | |
696 | // `[patch]`... | |
697 | if !dep.matches_ignoring_source(id) { | |
698 | return false; | |
699 | } | |
700 | ||
701 | // ... so here we look up the dependency url in the patches | |
702 | // map, and we see if `id` is contained in the list of patches | |
703 | // for that url. If it is then this lock is still valid, | |
704 | // otherwise the lock is no longer valid. | |
e5454122 | 705 | match patches.get(dep.source_id().canonical_url()) { |
803bf329 AC |
706 | Some(list) => list.contains(&id), |
707 | None => false, | |
708 | } | |
709 | }); | |
710 | ||
dae87a26 | 711 | if let Some(&locked) = locked { |
e3835f70 | 712 | trace!("\tfirst hit on {}", locked); |
35a2b86a | 713 | let mut dep = dep; |
803bf329 AC |
714 | |
715 | // If we found a locked version where the sources match, then | |
716 | // we can `lock_to` to get an exact lock on this dependency. | |
717 | // Otherwise we got a lock via `[patch]` so we only lock the | |
718 | // version requirement, not the source. | |
719 | if locked.source_id() == dep.source_id() { | |
720 | dep.lock_to(locked); | |
721 | } else { | |
722 | let req = VersionReq::exact(locked.version()); | |
723 | dep.set_version_req(req); | |
724 | } | |
1e682848 | 725 | return dep; |
e3835f70 AC |
726 | } |
727 | } | |
728 | ||
729 | // If this dependency did not have a locked version, then we query | |
730 | // all known locked packages to see if they match this dependency. | |
731 | // If anything does then we lock it to that and move on. | |
1e682848 | 732 | let v = locked |
8418a3ae | 733 | .get(&(dep.source_id(), dep.package_name())) |
dae87a26 E |
734 | .and_then(|vec| vec.iter().find(|&&(id, _)| dep.matches_id(id))); |
735 | if let Some(&(id, _)) = v { | |
61a3c68b | 736 | trace!("\tsecond hit on {}", id); |
35a2b86a | 737 | let mut dep = dep; |
61a3c68b | 738 | dep.lock_to(id); |
1e682848 | 739 | return dep; |
61a3c68b AC |
740 | } |
741 | ||
61a3c68b | 742 | trace!("\tnope, unlocked"); |
23591fe5 | 743 | dep |
e3835f70 AC |
744 | }) |
745 | } | |
b78cb373 | 746 | |
afa3aced | 747 | /// This is a helper for selecting the summary, or generating a helpful error message. |
b78cb373 EH |
748 | fn summary_for_patch( |
749 | orig_patch: &Dependency, | |
afa3aced | 750 | locked: &Option<(Dependency, PackageId)>, |
b78cb373 EH |
751 | mut summaries: Vec<Summary>, |
752 | source: &mut dyn Source, | |
afa3aced | 753 | ) -> CargoResult<(Summary, Option<PackageId>)> { |
b78cb373 | 754 | if summaries.len() == 1 { |
afa3aced | 755 | return Ok((summaries.pop().unwrap(), None)); |
b78cb373 | 756 | } |
b78cb373 | 757 | if summaries.len() > 1 { |
4f2bae93 EH |
758 | // TODO: In the future, it might be nice to add all of these |
759 | // candidates so that version selection would just pick the | |
760 | // appropriate one. However, as this is currently structured, if we | |
761 | // added these all as patches, the unselected versions would end up in | |
762 | // the "unused patch" listing, and trigger a warning. It might take a | |
763 | // fair bit of restructuring to make that work cleanly, and there | |
764 | // isn't any demand at this time to support that. | |
765 | let mut vers: Vec<_> = summaries.iter().map(|summary| summary.version()).collect(); | |
766 | vers.sort(); | |
767 | let versions: Vec<_> = vers.into_iter().map(|v| v.to_string()).collect(); | |
768 | anyhow::bail!( | |
769 | "patch for `{}` in `{}` resolved to more than one candidate\n\ | |
770 | Found versions: {}\n\ | |
771 | Update the patch definition to select only one package.\n\ | |
772 | For example, add an `=` version requirement to the patch definition, \ | |
773 | such as `version = \"={}\"`.", | |
774 | orig_patch.package_name(), | |
775 | orig_patch.source_id(), | |
776 | versions.join(", "), | |
777 | versions.last().unwrap() | |
778 | ); | |
b78cb373 | 779 | } |
afa3aced | 780 | assert!(summaries.is_empty()); |
b78cb373 | 781 | // No summaries found, try to help the user figure out what is wrong. |
6fd11a77 | 782 | if let Some((_locked_patch, locked_id)) = locked { |
afa3aced | 783 | // Since the locked patch did not match anything, try the unlocked one. |
6fd11a77 | 784 | let orig_matches = source.query_vec(orig_patch).unwrap_or_else(|e| { |
2c0cd974 EH |
785 | log::warn!( |
786 | "could not determine unlocked summaries for dep {:?}: {:?}", | |
787 | orig_patch, | |
788 | e | |
789 | ); | |
790 | Vec::new() | |
791 | }); | |
6fd11a77 | 792 | let (summary, _) = summary_for_patch(orig_patch, &None, orig_matches, source)?; |
afa3aced EH |
793 | // The unlocked version found a match. This returns a value to |
794 | // indicate that this entry should be unlocked. | |
795 | return Ok((summary, Some(*locked_id))); | |
6fd11a77 EH |
796 | } |
797 | // Try checking if there are *any* packages that match this by name. | |
798 | let name_only_dep = Dependency::new_override(orig_patch.package_name(), orig_patch.source_id()); | |
799 | let name_summaries = source.query_vec(&name_only_dep).unwrap_or_else(|e| { | |
800 | log::warn!( | |
801 | "failed to do name-only summary query for {:?}: {:?}", | |
802 | name_only_dep, | |
803 | e | |
804 | ); | |
805 | Vec::new() | |
806 | }); | |
807 | let mut vers = name_summaries | |
808 | .iter() | |
809 | .map(|summary| summary.version()) | |
810 | .collect::<Vec<_>>(); | |
811 | let found = match vers.len() { | |
812 | 0 => format!(""), | |
813 | 1 => format!("version `{}`", vers[0]), | |
814 | _ => { | |
815 | vers.sort(); | |
816 | let strs: Vec<_> = vers.into_iter().map(|v| v.to_string()).collect(); | |
817 | format!("versions `{}`", strs.join(", ")) | |
b78cb373 | 818 | } |
6fd11a77 EH |
819 | }; |
820 | if found.is_empty() { | |
821 | anyhow::bail!( | |
5dde9cc9 | 822 | "The patch location `{}` does not appear to contain any packages \ |
6fd11a77 | 823 | matching the name `{}`.", |
5dde9cc9 | 824 | orig_patch.source_id(), |
6fd11a77 EH |
825 | orig_patch.package_name() |
826 | ); | |
827 | } else { | |
828 | anyhow::bail!( | |
5dde9cc9 | 829 | "The patch location `{}` contains a `{}` package with {}, but the patch \ |
6fd11a77 EH |
830 | definition requires `{}`.\n\ |
831 | Check that the version in the patch location is what you expect, \ | |
832 | and update the patch definition to match.", | |
5dde9cc9 | 833 | orig_patch.source_id(), |
6fd11a77 EH |
834 | orig_patch.package_name(), |
835 | found, | |
836 | orig_patch.version_req() | |
837 | ); | |
afa3aced | 838 | } |
b78cb373 | 839 | } |