Skip to content

Commit ffab519

Browse files
committed
Auto merge of #4123 - alexcrichton:augment, r=matklad
Implement the [patch] section of the manifest This is an implementation of [RFC 1969] which adds a new section to top-level manifests: `[patch]`. This section allows you to augment existing sources with new versions of crates, possibly replacing the versions that already exist in the source. More details about this feature can be found in the RFC itself. [RFC 1969]: rust-lang/rfcs#1969
2 parents 8bbb703 + 61a3c68 commit ffab519

File tree

14 files changed

+1356
-181
lines changed

14 files changed

+1356
-181
lines changed

src/cargo/core/dependency.rs

+12
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ impl Dependency {
258258
.set_source_id(id.source_id().clone())
259259
}
260260

261+
/// Returns whether this is a "locked" dependency, basically whether it has
262+
/// an exact version req.
263+
pub fn is_locked(&self) -> bool {
264+
// Kind of a hack to figure this out, but it works!
265+
self.inner.req.to_string().starts_with("=")
266+
}
261267

262268
/// Returns false if the dependency is only used to build the local package.
263269
pub fn is_transitive(&self) -> bool {
@@ -292,6 +298,12 @@ impl Dependency {
292298
self.matches_id(sum.package_id())
293299
}
294300

301+
/// Returns true if the package (`sum`) can fulfill this dependency request.
302+
pub fn matches_ignoring_source(&self, sum: &Summary) -> bool {
303+
self.name() == sum.package_id().name() &&
304+
self.version_req().matches(sum.package_id().version())
305+
}
306+
295307
/// Returns true if the package (`id`) can fulfill this dependency request.
296308
pub fn matches_id(&self, id: &PackageId) -> bool {
297309
self.inner.name == id.name() &&

src/cargo/core/manifest.rs

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::rc::Rc;
55

66
use semver::Version;
77
use serde::ser;
8+
use url::Url;
89

910
use core::{Dependency, PackageId, Summary, SourceId, PackageIdSpec};
1011
use core::WorkspaceConfig;
@@ -28,13 +29,15 @@ pub struct Manifest {
2829
profiles: Profiles,
2930
publish: bool,
3031
replace: Vec<(PackageIdSpec, Dependency)>,
32+
patch: HashMap<Url, Vec<Dependency>>,
3133
workspace: WorkspaceConfig,
3234
original: Rc<TomlManifest>,
3335
}
3436

3537
#[derive(Clone, Debug)]
3638
pub struct VirtualManifest {
3739
replace: Vec<(PackageIdSpec, Dependency)>,
40+
patch: HashMap<Url, Vec<Dependency>>,
3841
workspace: WorkspaceConfig,
3942
profiles: Profiles,
4043
}
@@ -225,6 +228,7 @@ impl Manifest {
225228
profiles: Profiles,
226229
publish: bool,
227230
replace: Vec<(PackageIdSpec, Dependency)>,
231+
patch: HashMap<Url, Vec<Dependency>>,
228232
workspace: WorkspaceConfig,
229233
original: Rc<TomlManifest>) -> Manifest {
230234
Manifest {
@@ -238,6 +242,7 @@ impl Manifest {
238242
profiles: profiles,
239243
publish: publish,
240244
replace: replace,
245+
patch: patch,
241246
workspace: workspace,
242247
original: original,
243248
}
@@ -257,6 +262,7 @@ impl Manifest {
257262
pub fn publish(&self) -> bool { self.publish }
258263
pub fn replace(&self) -> &[(PackageIdSpec, Dependency)] { &self.replace }
259264
pub fn original(&self) -> &TomlManifest { &self.original }
265+
pub fn patch(&self) -> &HashMap<Url, Vec<Dependency>> { &self.patch }
260266
pub fn links(&self) -> Option<&str> {
261267
self.links.as_ref().map(|s| &s[..])
262268
}
@@ -284,10 +290,12 @@ impl Manifest {
284290

285291
impl VirtualManifest {
286292
pub fn new(replace: Vec<(PackageIdSpec, Dependency)>,
293+
patch: HashMap<Url, Vec<Dependency>>,
287294
workspace: WorkspaceConfig,
288295
profiles: Profiles) -> VirtualManifest {
289296
VirtualManifest {
290297
replace: replace,
298+
patch: patch,
291299
workspace: workspace,
292300
profiles: profiles,
293301
}
@@ -297,6 +305,10 @@ impl VirtualManifest {
297305
&self.replace
298306
}
299307

308+
pub fn patch(&self) -> &HashMap<Url, Vec<Dependency>> {
309+
&self.patch
310+
}
311+
300312
pub fn workspace_config(&self) -> &WorkspaceConfig {
301313
&self.workspace
302314
}

src/cargo/core/registry.rs

+164-39
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use std::collections::HashMap;
22

3+
use semver::VersionReq;
4+
use url::Url;
5+
36
use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId};
47
use core::PackageSet;
58
use util::{Config, profile};
@@ -77,6 +80,7 @@ pub struct PackageRegistry<'cfg> {
7780

7881
locked: LockedMap,
7982
source_config: SourceConfigMap<'cfg>,
83+
patches: HashMap<Url, Vec<Summary>>,
8084
}
8185

8286
type LockedMap = HashMap<SourceId, HashMap<String, Vec<(PackageId, Vec<PackageId>)>>>;
@@ -97,6 +101,7 @@ impl<'cfg> PackageRegistry<'cfg> {
97101
overrides: Vec::new(),
98102
source_config: source_config,
99103
locked: HashMap::new(),
104+
patches: HashMap::new(),
100105
})
101106
}
102107

@@ -175,6 +180,39 @@ impl<'cfg> PackageRegistry<'cfg> {
175180
sub_vec.push((id, deps));
176181
}
177182

183+
pub fn patch(&mut self, url: &Url, deps: &[Dependency]) -> CargoResult<()> {
184+
let deps = deps.iter().map(|dep| {
185+
let mut summaries = self.query_vec(dep)?.into_iter();
186+
let summary = match summaries.next() {
187+
Some(summary) => summary,
188+
None => {
189+
bail!("patch for `{}` in `{}` did not resolve to any crates",
190+
dep.name(), url)
191+
}
192+
};
193+
if summaries.next().is_some() {
194+
bail!("patch for `{}` in `{}` resolved to more than one candidate",
195+
dep.name(), url)
196+
}
197+
if summary.package_id().source_id().url() == url {
198+
bail!("patch for `{}` in `{}` points to the same source, but \
199+
patches must point to different sources",
200+
dep.name(), url);
201+
}
202+
Ok(summary)
203+
}).collect::<CargoResult<Vec<_>>>().chain_err(|| {
204+
format!("failed to resolve patches for `{}`", url)
205+
})?;
206+
207+
self.patches.insert(url.clone(), deps);
208+
209+
Ok(())
210+
}
211+
212+
pub fn patches(&self) -> &HashMap<Url, Vec<Summary>> {
213+
&self.patches
214+
}
215+
178216
fn load(&mut self, source_id: &SourceId, kind: Kind) -> CargoResult<()> {
179217
(|| {
180218
let source = self.source_config.load(source_id)?;
@@ -222,7 +260,7 @@ impl<'cfg> PackageRegistry<'cfg> {
222260
/// possible. If we're unable to map a dependency though, we just pass it on
223261
/// through.
224262
pub fn lock(&self, summary: Summary) -> Summary {
225-
lock(&self.locked, summary)
263+
lock(&self.locked, &self.patches, summary)
226264
}
227265

228266
fn warn_bad_override(&self,
@@ -274,39 +312,97 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
274312
fn query(&mut self,
275313
dep: &Dependency,
276314
f: &mut FnMut(Summary)) -> CargoResult<()> {
277-
// Ensure the requested source_id is loaded
278-
self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| {
279-
format!("failed to load source for a dependency \
280-
on `{}`", dep.name())
281-
})?;
282-
283-
284315
let (override_summary, n, to_warn) = {
285316
// Look for an override and get ready to query the real source.
286317
let override_summary = self.query_overrides(&dep)?;
287-
let source = self.sources.get_mut(dep.source_id());
288-
match (override_summary, source) {
289-
(Some(_), None) => bail!("override found but no real ones"),
290-
(None, None) => return Ok(()),
291-
292-
// If we don't have an override then we just ship everything
293-
// upstairs after locking the summary
294-
(None, Some(source)) => {
295-
let locked = &self.locked;
296-
return source.query(dep, &mut |summary| f(lock(locked, summary)))
318+
319+
// Next up on our list of candidates is to check the `[patch]`
320+
// section of the manifest. Here we look through all patches
321+
// relevant to the source that `dep` points to, and then we match
322+
// name/version. Note that we don't use `dep.matches(..)` because
323+
// the patches, by definition, come from a different source.
324+
// This means that `dep.matches(..)` will always return false, when
325+
// what we really care about is the name/version match.
326+
let mut patches = Vec::<Summary>::new();
327+
if let Some(extra) = self.patches.get(dep.source_id().url()) {
328+
patches.extend(extra.iter().filter(|s| {
329+
dep.matches_ignoring_source(s)
330+
}).cloned());
331+
}
332+
333+
// A crucial feature of the `[patch]` feature is that we *don't*
334+
// query the actual registry if we have a "locked" dependency. A
335+
// locked dep basically just means a version constraint of `=a.b.c`,
336+
// and because patches take priority over the actual source then if
337+
// we have a candidate we're done.
338+
if patches.len() == 1 && dep.is_locked() {
339+
let patch = patches.remove(0);
340+
match override_summary {
341+
Some(summary) => (summary, 1, Some(patch)),
342+
None => {
343+
f(patch);
344+
return Ok(())
345+
}
297346
}
347+
} else {
348+
if patches.len() > 0 {
349+
debug!("found {} patches with an unlocked dep, \
350+
looking at sources", patches.len());
351+
}
352+
353+
// Ensure the requested source_id is loaded
354+
self.ensure_loaded(dep.source_id(), Kind::Normal).chain_err(|| {
355+
format!("failed to load source for a dependency \
356+
on `{}`", dep.name())
357+
})?;
358+
359+
let source = self.sources.get_mut(dep.source_id());
360+
match (override_summary, source) {
361+
(Some(_), None) => bail!("override found but no real ones"),
362+
(None, None) => return Ok(()),
363+
364+
// If we don't have an override then we just ship
365+
// everything upstairs after locking the summary
366+
(None, Some(source)) => {
367+
for patch in patches.iter() {
368+
f(patch.clone());
369+
}
370+
371+
// Our sources shouldn't ever come back to us with two
372+
// summaries that have the same version. We could,
373+
// however, have an `[patch]` section which is in use
374+
// to override a version in the registry. This means
375+
// that if our `summary` in this loop has the same
376+
// version as something in `patches` that we've
377+
// already selected, then we skip this `summary`.
378+
let locked = &self.locked;
379+
let all_patches = &self.patches;
380+
return source.query(dep, &mut |summary| {
381+
for patch in patches.iter() {
382+
let patch = patch.package_id().version();
383+
if summary.package_id().version() == patch {
384+
return
385+
}
386+
}
387+
f(lock(locked, all_patches, summary))
388+
})
389+
}
298390

299-
// If we have an override summary then we query the source to sanity
300-
// check its results. We don't actually use any of the summaries it
301-
// gives us though.
302-
(Some(override_summary), Some(source)) => {
303-
let mut n = 0;
304-
let mut to_warn = None;
305-
source.query(dep, &mut |summary| {
306-
n += 1;
307-
to_warn = Some(summary);
308-
})?;
309-
(override_summary, n, to_warn)
391+
// If we have an override summary then we query the source
392+
// to sanity check its results. We don't actually use any of
393+
// the summaries it gives us though.
394+
(Some(override_summary), Some(source)) => {
395+
if patches.len() > 0 {
396+
bail!("found patches and a path override")
397+
}
398+
let mut n = 0;
399+
let mut to_warn = None;
400+
source.query(dep, &mut |summary| {
401+
n += 1;
402+
to_warn = Some(summary);
403+
})?;
404+
(override_summary, n, to_warn)
405+
}
310406
}
311407
}
312408
};
@@ -321,7 +417,9 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
321417
}
322418
}
323419

324-
fn lock(locked: &LockedMap, summary: Summary) -> Summary {
420+
fn lock(locked: &LockedMap,
421+
patches: &HashMap<Url, Vec<Summary>>,
422+
summary: Summary) -> Summary {
325423
let pair = locked.get(summary.source_id()).and_then(|map| {
326424
map.get(summary.name())
327425
}).and_then(|vec| {
@@ -335,7 +433,7 @@ fn lock(locked: &LockedMap, summary: Summary) -> Summary {
335433
Some(&(ref precise, _)) => summary.override_id(precise.clone()),
336434
None => summary,
337435
};
338-
summary.map_dependencies(|mut dep| {
436+
summary.map_dependencies(|dep| {
339437
trace!("\t{}/{}/{}", dep.name(), dep.version_req(),
340438
dep.source_id());
341439

@@ -362,6 +460,7 @@ fn lock(locked: &LockedMap, summary: Summary) -> Summary {
362460
let locked = locked_deps.iter().find(|id| dep.matches_id(id));
363461
if let Some(locked) = locked {
364462
trace!("\tfirst hit on {}", locked);
463+
let mut dep = dep.clone();
365464
dep.lock_to(locked);
366465
return dep
367466
}
@@ -375,17 +474,43 @@ fn lock(locked: &LockedMap, summary: Summary) -> Summary {
375474
}).and_then(|vec| {
376475
vec.iter().find(|&&(ref id, _)| dep.matches_id(id))
377476
});
378-
match v {
379-
Some(&(ref id, _)) => {
380-
trace!("\tsecond hit on {}", id);
381-
dep.lock_to(id);
477+
if let Some(&(ref id, _)) = v {
478+
trace!("\tsecond hit on {}", id);
479+
let mut dep = dep.clone();
480+
dep.lock_to(id);
481+
return dep
482+
}
483+
484+
// Finally we check to see if any registered patches correspond to
485+
// this dependency.
486+
let v = patches.get(dep.source_id().url()).map(|vec| {
487+
let dep2 = dep.clone();
488+
let mut iter = vec.iter().filter(move |s| {
489+
dep2.name() == s.package_id().name() &&
490+
dep2.version_req().matches(s.package_id().version())
491+
});
492+
(iter.next(), iter)
493+
});
494+
if let Some((Some(summary), mut remaining)) = v {
495+
assert!(remaining.next().is_none());
496+
let patch_source = summary.package_id().source_id();
497+
let patch_locked = locked.get(patch_source).and_then(|m| {
498+
m.get(summary.package_id().name())
499+
}).map(|list| {
500+
list.iter().any(|&(ref id, _)| id == summary.package_id())
501+
}).unwrap_or(false);
502+
503+
if patch_locked {
504+
trace!("\tthird hit on {}", summary.package_id());
505+
let req = VersionReq::exact(summary.package_id().version());
506+
let mut dep = dep.clone();
507+
dep.set_version_req(req);
382508
return dep
383509
}
384-
None => {
385-
trace!("\tremaining unlocked");
386-
dep
387-
}
388510
}
511+
512+
trace!("\tnope, unlocked");
513+
return dep
389514
})
390515
}
391516

0 commit comments

Comments
 (0)