Skip to content

Commit c7ce876

Browse files
committed
Auto merge of #4949 - alexcrichton:fix-patch-network, r=matklad
Fix `[patch]` sections depending on one another This commit fixes a bug in `[patch]` where the original source is updated too often (for example `Updating ...` being printed too much). This bug occurred when patches depended on each other (for example the dependencies of a resolved `[patch]` would actually resolve to a `[patch]` that hadn't been resolved yet). The ordering of resolution/locking wasn't happening correctly and wasn't ready to break these cycles! The process of adding `[patch]` sections to a registry has been updated to account for this bug. Instead of add-and-lock all in one go this commit instead splits the addition of `[patch]` into two phases. In the first we collect a bunch of unlocked patch summaries but record all the `PackageId` instances for each url we've scraped. After all `[patch]` sections have been processed in this manner we go back and lock all the summaries that were previously unlocked. The `lock` function was updated to *only* need the map of patches from URL to `PackageId` as it doesn't actually have access to the full `Summary` of patches during the `lock_patches` method. All in all this should correctly resolve dependencies here which means that processing of patches should proceed as usual, avoiding updating the registry too much! Closes #4941
2 parents 36629e7 + eebddd3 commit c7ce876

File tree

5 files changed

+213
-50
lines changed

5 files changed

+213
-50
lines changed

src/cargo/core/dependency.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,11 @@ impl Dependency {
254254
pub fn lock_to(&mut self, id: &PackageId) -> &mut Dependency {
255255
assert_eq!(self.inner.source_id, *id.source_id());
256256
assert!(self.inner.req.matches(id.version()));
257+
trace!("locking dep from `{}` with `{}` at {} to {}",
258+
self.name(),
259+
self.version_req(),
260+
self.source_id(),
261+
id);
257262
self.set_version_req(VersionReq::exact(id.version()))
258263
.set_source_id(id.source_id().clone())
259264
}

src/cargo/core/registry.rs

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ pub struct PackageRegistry<'cfg> {
8888

8989
locked: LockedMap,
9090
source_config: SourceConfigMap<'cfg>,
91+
9192
patches: HashMap<Url, Vec<Summary>>,
93+
patches_locked: bool,
94+
patches_available: HashMap<Url, Vec<PackageId>>,
9295
}
9396

9497
type LockedMap = HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>;
@@ -110,6 +113,8 @@ impl<'cfg> PackageRegistry<'cfg> {
110113
source_config: source_config,
111114
locked: HashMap::new(),
112115
patches: HashMap::new(),
116+
patches_locked: false,
117+
patches_available: HashMap::new(),
113118
})
114119
}
115120

@@ -188,9 +193,50 @@ impl<'cfg> PackageRegistry<'cfg> {
188193
sub_vec.push((id, deps));
189194
}
190195

196+
/// Insert a `[patch]` section into this registry.
197+
///
198+
/// This method will insert a `[patch]` section for the `url` specified,
199+
/// with the given list of dependencies. The `url` specified is the URL of
200+
/// the source to patch (for example this is `crates-io` in the manifest).
201+
/// The `deps` is an array of all the entries in the `[patch]` section of
202+
/// the manifest.
203+
///
204+
/// Here the `deps` will be resolved to a precise version and stored
205+
/// internally for future calls to `query` below. It's expected that `deps`
206+
/// have had `lock_to` call already, if applicable. (e.g. if a lock file was
207+
/// already present).
208+
///
209+
/// Note that the patch list specified here *will not* be available to
210+
/// `query` until `lock_patches` is called below, which should be called
211+
/// once all patches have been added.
191212
pub fn patch(&mut self, url: &Url, deps: &[Dependency]) -> CargoResult<()> {
192-
let deps = deps.iter().map(|dep| {
193-
let mut summaries = self.query_vec(dep)?.into_iter();
213+
// First up we need to actually resolve each `deps` specification to
214+
// precisely one summary. We're not using the `query` method below as it
215+
// internally uses maps we're building up as part of this method
216+
// (`patches_available` and `patches). Instead we're going straight to
217+
// the source to load information from it.
218+
//
219+
// Remember that each dependency listed in `[patch]` has to resolve to
220+
// precisely one package, so that's why we're just creating a flat list
221+
// of summaries which should be the same length as `deps` above.
222+
let unlocked_summaries = deps.iter().map(|dep| {
223+
debug!("registring a patch for `{}` with `{}`",
224+
url,
225+
dep.name());
226+
227+
// Go straight to the source for resolving `dep`. Load it as we
228+
// normally would and then ask it directly for the list of summaries
229+
// corresponding to this `dep`.
230+
self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| {
231+
format_err!("failed to load source for a dependency \
232+
on `{}`", dep.name())
233+
})?;
234+
235+
let mut summaries = self.sources.get_mut(dep.source_id())
236+
.expect("loaded source not present")
237+
.query_vec(dep)?
238+
.into_iter();
239+
194240
let summary = match summaries.next() {
195241
Some(summary) => summary,
196242
None => {
@@ -214,11 +260,38 @@ impl<'cfg> PackageRegistry<'cfg> {
214260
format_err!("failed to resolve patches for `{}`", url)
215261
})?;
216262

217-
self.patches.insert(url.clone(), deps);
263+
// Note that we do not use `lock` here to lock summaries! That step
264+
// happens later once `lock_patches` is invoked. In the meantime though
265+
// we want to fill in the `patches_available` map (later used in the
266+
// `lock` method) and otherwise store the unlocked summaries in
267+
// `patches` to get locked in a future call to `lock_patches`.
268+
let ids = unlocked_summaries.iter()
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);
218274

219275
Ok(())
220276
}
221277

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+
222295
pub fn patches(&self) -> &HashMap<Url, Vec<Summary>> {
223296
&self.patches
224297
}
@@ -271,7 +344,8 @@ impl<'cfg> PackageRegistry<'cfg> {
271344
/// possible. If we're unable to map a dependency though, we just pass it on
272345
/// through.
273346
pub fn lock(&self, summary: Summary) -> Summary {
274-
lock(&self.locked, &self.patches, summary)
347+
assert!(self.patches_locked);
348+
lock(&self.locked, &self.patches_available, summary)
275349
}
276350

277351
fn warn_bad_override(&self,
@@ -323,6 +397,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
323397
fn query(&mut self,
324398
dep: &Dependency,
325399
f: &mut FnMut(Summary)) -> CargoResult<()> {
400+
assert!(self.patches_locked);
326401
let (override_summary, n, to_warn) = {
327402
// Look for an override and get ready to query the real source.
328403
let override_summary = self.query_overrides(dep)?;
@@ -357,8 +432,12 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
357432
}
358433
} else {
359434
if !patches.is_empty() {
360-
debug!("found {} patches with an unlocked dep, \
361-
looking at sources", patches.len());
435+
debug!("found {} patches with an unlocked dep on `{}` at {} \
436+
with `{}`, \
437+
looking at sources", patches.len(),
438+
dep.name(),
439+
dep.source_id(),
440+
dep.version_req());
362441
}
363442

364443
// Ensure the requested source_id is loaded
@@ -387,7 +466,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
387466
// version as something in `patches` that we've
388467
// already selected, then we skip this `summary`.
389468
let locked = &self.locked;
390-
let all_patches = &self.patches;
469+
let all_patches = &self.patches_available;
391470
return source.query(dep, &mut |summary| {
392471
for patch in patches.iter() {
393472
let patch = patch.package_id().version();
@@ -437,7 +516,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
437516
}
438517

439518
fn lock(locked: &LockedMap,
440-
patches: &HashMap<Url, Vec<Summary>>,
519+
patches: &HashMap<Url, Vec<PackageId>>,
441520
summary: Summary) -> Summary {
442521
let pair = locked.get(summary.source_id()).and_then(|map| {
443522
map.get(summary.name())
@@ -504,24 +583,24 @@ fn lock(locked: &LockedMap,
504583
// this dependency.
505584
let v = patches.get(dep.source_id().url()).map(|vec| {
506585
let dep2 = dep.clone();
507-
let mut iter = vec.iter().filter(move |s| {
508-
dep2.name() == s.package_id().name() &&
509-
dep2.version_req().matches(s.package_id().version())
586+
let mut iter = vec.iter().filter(move |p| {
587+
dep2.name() == p.name() &&
588+
dep2.version_req().matches(p.version())
510589
});
511590
(iter.next(), iter)
512591
});
513-
if let Some((Some(summary), mut remaining)) = v {
592+
if let Some((Some(patch_id), mut remaining)) = v {
514593
assert!(remaining.next().is_none());
515-
let patch_source = summary.package_id().source_id();
594+
let patch_source = patch_id.source_id();
516595
let patch_locked = locked.get(patch_source).and_then(|m| {
517-
m.get(summary.package_id().name())
596+
m.get(patch_id.name())
518597
}).map(|list| {
519-
list.iter().any(|&(ref id, _)| id == summary.package_id())
598+
list.iter().any(|&(ref id, _)| id == patch_id)
520599
}).unwrap_or(false);
521600

522601
if patch_locked {
523-
trace!("\tthird hit on {}", summary.package_id());
524-
let req = VersionReq::exact(summary.package_id().version());
602+
trace!("\tthird hit on {}", patch_id);
603+
let req = VersionReq::exact(patch_id.version());
525604
let mut dep = dep.clone();
526605
dep.set_version_req(req);
527606
return dep

src/cargo/ops/cargo_generate_lockfile.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ pub struct UpdateOptions<'a> {
1919

2020
pub fn generate_lockfile(ws: &Workspace) -> CargoResult<()> {
2121
let mut registry = PackageRegistry::new(ws.config())?;
22-
let resolve = ops::resolve_with_previous(&mut registry, ws,
22+
let resolve = ops::resolve_with_previous(&mut registry,
23+
ws,
2324
Method::Everything,
24-
None, None, &[], true)?;
25+
None,
26+
None,
27+
&[],
28+
true,
29+
true)?;
2530
ops::write_pkg_lockfile(ws, &resolve)?;
2631
Ok(())
2732
}
@@ -81,12 +86,13 @@ pub fn update_lockfile(ws: &Workspace, opts: &UpdateOptions)
8186
}
8287

8388
let resolve = ops::resolve_with_previous(&mut registry,
84-
ws,
85-
Method::Everything,
86-
Some(&previous_resolve),
87-
Some(&to_avoid),
88-
&[],
89-
true)?;
89+
ws,
90+
Method::Everything,
91+
Some(&previous_resolve),
92+
Some(&to_avoid),
93+
&[],
94+
true,
95+
true)?;
9096

9197
// Summarize what is changing for the user.
9298
let print_change = |status: &str, msg: String, color: Color| {

src/cargo/ops/resolve.rs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ pub fn resolve_ws_precisely<'a>(ws: &Workspace<'a>,
4040
if let Some(source) = source {
4141
registry.add_preloaded(source);
4242
}
43+
let mut add_patches = true;
4344

4445
let resolve = if ws.require_optional_deps() {
4546
// First, resolve the root_package's *listed* dependencies, as well as
4647
// downloading and updating all remotes and such.
4748
let resolve = resolve_with_registry(ws, &mut registry, false)?;
49+
add_patches = false;
4850

4951
// Second, resolve with precisely what we're doing. Filter out
5052
// transitive dependencies if necessary, specify features, handle
@@ -77,9 +79,14 @@ pub fn resolve_ws_precisely<'a>(ws: &Workspace<'a>,
7779
};
7880

7981
let resolved_with_overrides =
80-
ops::resolve_with_previous(&mut registry, ws,
81-
method, resolve.as_ref(), None,
82-
specs, true)?;
82+
ops::resolve_with_previous(&mut registry,
83+
ws,
84+
method,
85+
resolve.as_ref(),
86+
None,
87+
specs,
88+
add_patches,
89+
true)?;
8390

8491
let packages = get_resolved_packages(&resolved_with_overrides, registry);
8592

@@ -89,9 +96,14 @@ pub fn resolve_ws_precisely<'a>(ws: &Workspace<'a>,
8996
fn resolve_with_registry(ws: &Workspace, registry: &mut PackageRegistry, warn: bool)
9097
-> CargoResult<Resolve> {
9198
let prev = ops::load_pkg_lockfile(ws)?;
92-
let resolve = resolve_with_previous(registry, ws,
99+
let resolve = resolve_with_previous(registry,
100+
ws,
93101
Method::Everything,
94-
prev.as_ref(), None, &[], warn)?;
102+
prev.as_ref(),
103+
None,
104+
&[],
105+
true,
106+
warn)?;
95107

96108
if !ws.is_ephemeral() {
97109
ops::write_pkg_lockfile(ws, &resolve)?;
@@ -115,6 +127,7 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry,
115127
previous: Option<&'a Resolve>,
116128
to_avoid: Option<&HashSet<&'a PackageId>>,
117129
specs: &[PackageIdSpec],
130+
register_patches: bool,
118131
warn: bool)
119132
-> CargoResult<Resolve> {
120133
// Here we place an artificial limitation that all non-registry sources
@@ -169,27 +182,31 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry,
169182
}
170183
}
171184

172-
for (url, patches) in ws.root_patch() {
173-
let previous = match previous {
174-
Some(r) => r,
175-
None => {
176-
registry.patch(url, patches)?;
177-
continue
178-
}
179-
};
180-
let patches = patches.iter().map(|dep| {
181-
let unused = previous.unused_patches();
182-
let candidates = previous.iter().chain(unused);
183-
match candidates.filter(keep).find(|id| dep.matches_id(id)) {
184-
Some(id) => {
185-
let mut dep = dep.clone();
186-
dep.lock_to(id);
187-
dep
185+
if register_patches {
186+
for (url, patches) in ws.root_patch() {
187+
let previous = match previous {
188+
Some(r) => r,
189+
None => {
190+
registry.patch(url, patches)?;
191+
continue
188192
}
189-
None => dep.clone(),
190-
}
191-
}).collect::<Vec<_>>();
192-
registry.patch(url, &patches)?;
193+
};
194+
let patches = patches.iter().map(|dep| {
195+
let unused = previous.unused_patches();
196+
let candidates = previous.iter().chain(unused);
197+
match candidates.filter(keep).find(|id| dep.matches_id(id)) {
198+
Some(id) => {
199+
let mut dep = dep.clone();
200+
dep.lock_to(id);
201+
dep
202+
}
203+
None => dep.clone(),
204+
}
205+
}).collect::<Vec<_>>();
206+
registry.patch(url, &patches)?;
207+
}
208+
209+
registry.lock_patches();
193210
}
194211

195212
let mut summaries = Vec::new();

0 commit comments

Comments
 (0)