Skip to content

Commit 54d738b

Browse files
committed
Implement top-level overrides
This commit is an implementation of top-level overrides to be encoded into the manifest itself directly. This style of override is distinct from the existing `paths` support in `.cargo/config` in two important ways: * Top level overrides are intended to be checked in and shared amongst all developers of a project. * Top level overrides are reflected in `Cargo.lock`. The second point is crucially important here as it will ensure that an override on one machine behaves the same as an override on another machine. This solves many long-standing problems with `paths`-based overrides which suffer from some level of nondeterminism as they're not encoded. From a syntactical point of view, an override looks like: ```toml [replace] "libc:0.2.0" = { git = 'https://github.com/my-username/libc', branch = '0.2-fork' } ``` This declaration indicates that whenever resolution would otherwise encounter the `libc` package version 0.2.0 from crates.io, it should instead replace it with the custom git dependency on a specific branch. The key "libc:0.2.0" here is actually a package id specification which will allow selecting various components of a graph. For example the same named package coming from two distinct locations can be selected against, as well as multiple versions of one crate in a dependency graph. The replacement dependency has the same syntax as the `[dependencies]` section of Cargo.toml. One of the major uses of this syntax will be, for example, using a temporary fork of a crate while the changes are pushed upstream to the original repo. This will avoid the need to change the intermediate projects immediately, and over time once fixes have landed upstream the `[replace]` section in a `Cargo.toml` can be removed. There are also two crucial restrictions on overrides. * A crate with the name `foo` can only get overridden with packages also of the name `foo`. * A crate can only get overridden with a crate of the exact same version. A consequence of these restrictions is that crates.io cannot be used to replace anything from crates.io. There's only one version of something on crates.io, so there's nothing else to replace it with (name/version are a unique key). Closes #942
1 parent 4e6434d commit 54d738b

13 files changed

+878
-155
lines changed

src/cargo/core/manifest.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use std::default::Default;
21
use std::fmt;
32
use std::path::{PathBuf, Path};
43

54
use semver::Version;
65
use rustc_serialize::{Encoder, Encodable};
76

8-
use core::{Dependency, PackageId, Summary};
7+
use core::{Dependency, PackageId, PackageIdSpec, Summary};
98
use core::package_id::Metadata;
109
use util::{CargoResult, human};
1110

@@ -20,7 +19,8 @@ pub struct Manifest {
2019
include: Vec<String>,
2120
metadata: ManifestMetadata,
2221
profiles: Profiles,
23-
publish: bool
22+
publish: bool,
23+
replace: Vec<(PackageIdSpec, Dependency)>,
2424
}
2525

2626
/// General metadata about a package which is just blindly uploaded to the
@@ -165,7 +165,8 @@ impl Manifest {
165165
links: Option<String>,
166166
metadata: ManifestMetadata,
167167
profiles: Profiles,
168-
publish: bool) -> Manifest {
168+
publish: bool,
169+
replace: Vec<(PackageIdSpec, Dependency)>) -> Manifest {
169170
Manifest {
170171
summary: summary,
171172
targets: targets,
@@ -176,6 +177,7 @@ impl Manifest {
176177
metadata: metadata,
177178
profiles: profiles,
178179
publish: publish,
180+
replace: replace,
179181
}
180182
}
181183

@@ -191,6 +193,7 @@ impl Manifest {
191193
pub fn warnings(&self) -> &[String] { &self.warnings }
192194
pub fn profiles(&self) -> &Profiles { &self.profiles }
193195
pub fn publish(&self) -> bool { self.publish }
196+
pub fn replace(&self) -> &[(PackageIdSpec, Dependency)] { &self.replace }
194197
pub fn links(&self) -> Option<&str> {
195198
self.links.as_ref().map(|s| &s[..])
196199
}

src/cargo/core/registry.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl<'cfg> PackageRegistry<'cfg> {
7979
PackageRegistry {
8080
sources: SourceMap::new(),
8181
source_ids: HashMap::new(),
82-
overrides: vec![],
82+
overrides: Vec::new(),
8383
config: config,
8484
locked: HashMap::new(),
8585
}
@@ -270,18 +270,16 @@ impl<'cfg> PackageRegistry<'cfg> {
270270

271271
impl<'cfg> Registry for PackageRegistry<'cfg> {
272272
fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
273-
let overrides = try!(self.query_overrides(dep));
273+
let overrides = try!(self.query_overrides(&dep));
274274

275275
let ret = if overrides.is_empty() {
276276
// Ensure the requested source_id is loaded
277277
try!(self.ensure_loaded(dep.source_id(), Kind::Normal));
278-
let mut ret = Vec::new();
279-
for (id, src) in self.sources.sources_mut() {
280-
if id == dep.source_id() {
281-
ret.extend(try!(src.query(dep)).into_iter());
282-
}
278+
279+
match self.sources.get_mut(dep.source_id()) {
280+
Some(src) => try!(src.query(&dep)),
281+
None => Vec::new(),
283282
}
284-
ret
285283
} else {
286284
overrides
287285
};

src/cargo/core/resolver/encode.rs

+43-21
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,23 @@ impl EncodableResolve {
2626

2727
let mut g = Graph::new();
2828
let mut tmp = HashMap::new();
29+
let mut replacements = HashMap::new();
2930

3031
let packages = Vec::new();
3132
let packages = self.package.as_ref().unwrap_or(&packages);
3233

33-
let root = try!(to_package_id(&self.root.name,
34-
&self.root.version,
35-
self.root.source.as_ref(),
36-
default, &path_deps));
37-
let ids = try!(packages.iter().map(|p| {
38-
to_package_id(&p.name, &p.version, p.source.as_ref(),
34+
let id2pkgid = |id: &EncodablePackageId| {
35+
to_package_id(&id.name, &id.version, id.source.as_ref(),
3936
default, &path_deps)
40-
}).collect::<CargoResult<Vec<_>>>());
37+
};
38+
let dep2pkgid = |dep: &EncodableDependency| {
39+
to_package_id(&dep.name, &dep.version, dep.source.as_ref(),
40+
default, &path_deps)
41+
};
42+
43+
let root = try!(dep2pkgid(&self.root));
44+
let ids = try!(packages.iter().map(&dep2pkgid)
45+
.collect::<CargoResult<Vec<_>>>());
4146

4247
{
4348
let mut register_pkg = |pkgid: &PackageId| {
@@ -57,16 +62,22 @@ impl EncodableResolve {
5762
{
5863
let mut add_dependencies = |id: &PackageId, pkg: &EncodableDependency|
5964
-> CargoResult<()> {
65+
if let Some(ref replace) = pkg.replace {
66+
let replace = try!(id2pkgid(replace));
67+
let replace_precise = tmp.get(&replace).map(|p| {
68+
replace.with_precise(p.clone())
69+
}).unwrap_or(replace);
70+
replacements.insert(id.clone(), replace_precise);
71+
assert!(pkg.dependencies.is_none());
72+
return Ok(())
73+
}
74+
6075
let deps = match pkg.dependencies {
6176
Some(ref deps) => deps,
6277
None => return Ok(()),
6378
};
6479
for edge in deps.iter() {
65-
let to_depend_on = try!(to_package_id(&edge.name,
66-
&edge.version,
67-
edge.source.as_ref(),
68-
default,
69-
&path_deps));
80+
let to_depend_on = try!(id2pkgid(edge));
7081
let precise_pkgid =
7182
tmp.get(&to_depend_on)
7283
.map(|p| to_depend_on.with_precise(p.clone()))
@@ -87,6 +98,7 @@ impl EncodableResolve {
8798
root: root,
8899
features: HashMap::new(),
89100
metadata: self.metadata.clone(),
101+
replacements: replacements,
90102
})
91103
}
92104
}
@@ -136,7 +148,8 @@ pub struct EncodableDependency {
136148
name: String,
137149
version: String,
138150
source: Option<SourceId>,
139-
dependencies: Option<Vec<EncodablePackageId>>
151+
dependencies: Option<Vec<EncodablePackageId>>,
152+
replace: Option<EncodablePackageId>,
140153
}
141154

142155
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
@@ -186,24 +199,32 @@ impl Encodable for Resolve {
186199
let encodable = ids.iter().filter_map(|&id| {
187200
if self.root == *id { return None; }
188201

189-
Some(encodable_resolve_node(id, &self.graph))
202+
Some(encodable_resolve_node(id, self))
190203
}).collect::<Vec<EncodableDependency>>();
191204

192205
EncodableResolve {
193206
package: Some(encodable),
194-
root: encodable_resolve_node(&self.root, &self.graph),
207+
root: encodable_resolve_node(&self.root, self),
195208
metadata: self.metadata.clone(),
196209
}.encode(s)
197210
}
198211
}
199212

200-
fn encodable_resolve_node(id: &PackageId, graph: &Graph<PackageId>)
213+
fn encodable_resolve_node(id: &PackageId, resolve: &Resolve)
201214
-> EncodableDependency {
202-
let deps = graph.edges(id).map(|edge| {
203-
let mut deps = edge.map(encodable_package_id).collect::<Vec<_>>();
204-
deps.sort();
205-
deps
206-
});
215+
let (replace, deps) = match resolve.replacement(id) {
216+
Some(id) => {
217+
(Some(encodable_package_id(id)), None)
218+
}
219+
None => {
220+
let mut deps = resolve.graph.edges(id)
221+
.into_iter().flat_map(|a| a)
222+
.map(encodable_package_id)
223+
.collect::<Vec<_>>();
224+
deps.sort();
225+
(None, Some(deps))
226+
}
227+
};
207228

208229
let source = if id.source_id().is_path() {
209230
None
@@ -216,6 +237,7 @@ fn encodable_resolve_node(id: &PackageId, graph: &Graph<PackageId>)
216237
version: id.version().to_string(),
217238
source: source,
218239
dependencies: deps,
240+
replace: replace,
219241
}
220242
}
221243

0 commit comments

Comments
 (0)