Skip to content

Commit 2eade62

Browse files
committed
Auto merge of #2423 - alexcrichton:fix-pkgid-hash, r=brson
All crates being compiled by Cargo are identified by a unique `PackageId` instance. This ID incorporates information such as the name, version, and source from where the crate came from. Package ids are allowed to have path sources to depend on local crates on the filesystem. The package id itself encodes the path of where the crate came from. Historically, however, the "path source" from where these packages are learned had some interesting logic. Specifically one specific source would be able to return many packages within. In other words, a path source would recursively walk crate definitions and the filesystem attempting to find crates. Each crate returned from a source has the same source id, so consequently all packages from one source path would have the same source path id. This in turn leads to confusing an surprising behavior, for example: * When crates are compiled the status message indicates the path of the crate root, not the crate being compiled * When viewed from two different locations (e.g. two different crate roots) the same package would have two different source ids because the id is based on the root location. This hash mismatch has been [papered over](#1697) in the past to try to fix some spurious recompiles, but it unfortunately [leaked back in](#2279). This is clearly indicative of the "hack" being inappropriate so instead these commits fix the root of the problem. --- In short, these commits ensure that the package id for a package defined locally has a path that points precisely at that package. This was a relatively invasive change and had ramifications on a few specific portions which now require a bit of extra code to support. The fundamental change here was to change `PathSource` to be non-recursive by default in terms of what packages it thinks it contains. There are still two recursive use cases, git repositories and path overrides, which are used for backwards compatibility. This meant, however, that the packaging step for crate no longer has knowledge of other crates in a repository to filter out files from. Some specific logic was added to assist in discovering a git repository as well as filtering out sibling packages. Another ramification of this patch, however, is that special care needs to be taken when decoding a lockfile. We now need all path dependencies in the lockfile to point precisely at where the path dependency came from, and this information is not encoded in the lock file. The decoding support was altered to do a simple probe of the filesystem to recursively walk path dependencies to ensure that we can match up packages in a lock file to where they're found on the filesystem. Overall, however, this commit closes #1697 and also addresses servo/servo#9794 where this issue was originally reported.
2 parents dabc5a8 + d3d206d commit 2eade62

36 files changed

+423
-293
lines changed

src/bin/read_manifest.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use std::env;
22

3-
use cargo::core::{Package, Source};
3+
use cargo::core::Package;
44
use cargo::util::{CliResult, Config};
55
use cargo::util::important_paths::{find_root_manifest_for_wd};
6-
use cargo::sources::{PathSource};
76

87
#[derive(RustcDecodable)]
98
pub struct Options {
@@ -32,9 +31,6 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<Package>>
3231

3332
let root = try!(find_root_manifest_for_wd(options.flag_manifest_path, config.cwd()));
3433

35-
let mut source = try!(PathSource::for_path(root.parent().unwrap(), config));
36-
try!(source.update());
37-
38-
let pkg = try!(source.root_package());
34+
let pkg = try!(Package::for_path(&root, config));
3935
Ok(Some(pkg))
4036
}

src/cargo/core/package.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Package {
8787
}
8888

8989
pub fn generate_metadata(&self) -> Metadata {
90-
self.package_id().generate_metadata(self.root())
90+
self.package_id().generate_metadata()
9191
}
9292
}
9393

@@ -107,15 +107,7 @@ impl Eq for Package {}
107107

108108
impl hash::Hash for Package {
109109
fn hash<H: hash::Hasher>(&self, into: &mut H) {
110-
// We want to be sure that a path-based package showing up at the same
111-
// location always has the same hash. To that effect we don't hash the
112-
// vanilla package ID if we're a path, but instead feed in our own root
113-
// path.
114-
if self.package_id().source_id().is_path() {
115-
(0, self.root(), self.name(), self.package_id().version()).hash(into)
116-
} else {
117-
(1, self.package_id()).hash(into)
118-
}
110+
self.package_id().hash(into)
119111
}
120112
}
121113

src/cargo/core/package_id.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::error::Error;
33
use std::fmt::{self, Formatter};
44
use std::hash::Hash;
55
use std::hash;
6-
use std::path::Path;
76
use std::sync::Arc;
87

98
use regex::Regex;
@@ -136,13 +135,8 @@ impl PackageId {
136135
pub fn version(&self) -> &semver::Version { &self.inner.version }
137136
pub fn source_id(&self) -> &SourceId { &self.inner.source_id }
138137

139-
pub fn generate_metadata(&self, source_root: &Path) -> Metadata {
140-
// See comments in Package::hash for why we have this test
141-
let metadata = if self.inner.source_id.is_path() {
142-
short_hash(&(0, &self.inner.name, &self.inner.version, source_root))
143-
} else {
144-
short_hash(&(1, self))
145-
};
138+
pub fn generate_metadata(&self) -> Metadata {
139+
let metadata = short_hash(self);
146140
let extra_filename = format!("-{}", metadata);
147141

148142
Metadata { metadata: metadata, extra_filename: extra_filename }

src/cargo/core/registry.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,9 @@ impl<'cfg> PackageRegistry<'cfg> {
143143
self.source_ids.insert(id.clone(), (id.clone(), kind));
144144
}
145145

146-
pub fn add_overrides(&mut self, ids: Vec<SourceId>) -> CargoResult<()> {
147-
for id in ids.iter() {
148-
try!(self.load(id, Kind::Override));
149-
}
150-
Ok(())
146+
pub fn add_override(&mut self, id: &SourceId, source: Box<Source + 'cfg>) {
147+
self.add_source(id, source, Kind::Override);
148+
self.overrides.push(id.clone());
151149
}
152150

153151
pub fn register_lock(&mut self, id: PackageId, deps: Vec<PackageId>) {

src/cargo/core/resolver/encode.rs

+75-48
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::collections::{HashMap, BTreeMap};
33
use regex::Regex;
44
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
55

6-
use core::{PackageId, SourceId};
7-
use util::{CargoResult, Graph};
6+
use core::{Package, PackageId, SourceId};
7+
use util::{CargoResult, Graph, Config};
88

99
use super::Resolve;
1010

@@ -18,66 +18,113 @@ pub struct EncodableResolve {
1818
pub type Metadata = BTreeMap<String, String>;
1919

2020
impl EncodableResolve {
21-
pub fn to_resolve(&self, default: &SourceId) -> CargoResult<Resolve> {
21+
pub fn to_resolve(&self, root: &Package, config: &Config)
22+
-> CargoResult<Resolve> {
23+
let mut path_deps = HashMap::new();
24+
try!(build_path_deps(root, &mut path_deps, config));
25+
let default = root.package_id().source_id();
26+
2227
let mut g = Graph::new();
2328
let mut tmp = HashMap::new();
2429

2530
let packages = Vec::new();
2631
let packages = self.package.as_ref().unwrap_or(&packages);
2732

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(),
39+
default, &path_deps)
40+
}).collect::<CargoResult<Vec<_>>>());
41+
2842
{
29-
let mut register_pkg = |pkg: &EncodableDependency|
30-
-> CargoResult<()> {
31-
let pkgid = try!(pkg.to_package_id(default));
43+
let mut register_pkg = |pkgid: &PackageId| {
3244
let precise = pkgid.source_id().precise()
3345
.map(|s| s.to_string());
3446
assert!(tmp.insert(pkgid.clone(), precise).is_none(),
3547
"a package was referenced twice in the lockfile");
36-
g.add(try!(pkg.to_package_id(default)), &[]);
37-
Ok(())
48+
g.add(pkgid.clone(), &[]);
3849
};
3950

40-
try!(register_pkg(&self.root));
41-
for pkg in packages.iter() {
42-
try!(register_pkg(pkg));
51+
register_pkg(&root);
52+
for id in ids.iter() {
53+
register_pkg(id);
4354
}
4455
}
4556

4657
{
47-
let mut add_dependencies = |pkg: &EncodableDependency|
58+
let mut add_dependencies = |id: &PackageId, pkg: &EncodableDependency|
4859
-> CargoResult<()> {
49-
let package_id = try!(pkg.to_package_id(default));
50-
5160
let deps = match pkg.dependencies {
5261
Some(ref deps) => deps,
5362
None => return Ok(()),
5463
};
5564
for edge in deps.iter() {
56-
let to_depend_on = try!(edge.to_package_id(default));
65+
let to_depend_on = try!(to_package_id(&edge.name,
66+
&edge.version,
67+
edge.source.as_ref(),
68+
default,
69+
&path_deps));
5770
let precise_pkgid =
5871
tmp.get(&to_depend_on)
5972
.map(|p| to_depend_on.with_precise(p.clone()))
6073
.unwrap_or(to_depend_on.clone());
61-
g.link(package_id.clone(), precise_pkgid);
74+
g.link(id.clone(), precise_pkgid);
6275
}
6376
Ok(())
6477
};
6578

66-
try!(add_dependencies(&self.root));
67-
for pkg in packages.iter() {
68-
try!(add_dependencies(pkg));
79+
try!(add_dependencies(&root, &self.root));
80+
for (id, pkg) in ids.iter().zip(packages) {
81+
try!(add_dependencies(id, pkg));
6982
}
7083
}
7184

7285
Ok(Resolve {
7386
graph: g,
74-
root: try!(self.root.to_package_id(default)),
87+
root: root,
7588
features: HashMap::new(),
7689
metadata: self.metadata.clone(),
7790
})
7891
}
7992
}
8093

94+
fn build_path_deps(root: &Package,
95+
map: &mut HashMap<String, SourceId>,
96+
config: &Config)
97+
-> CargoResult<()> {
98+
assert!(root.package_id().source_id().is_path());
99+
100+
let deps = root.dependencies()
101+
.iter()
102+
.map(|d| d.source_id())
103+
.filter(|id| id.is_path())
104+
.filter_map(|id| id.url().to_file_path().ok())
105+
.map(|path| path.join("Cargo.toml"))
106+
.filter_map(|path| Package::for_path(&path, config).ok());
107+
for pkg in deps {
108+
let source_id = pkg.package_id().source_id();
109+
if map.insert(pkg.name().to_string(), source_id.clone()).is_none() {
110+
try!(build_path_deps(&pkg, map, config));
111+
}
112+
}
113+
114+
Ok(())
115+
}
116+
117+
fn to_package_id(name: &str,
118+
version: &str,
119+
source: Option<&SourceId>,
120+
default_source: &SourceId,
121+
path_sources: &HashMap<String, SourceId>)
122+
-> CargoResult<PackageId> {
123+
let source = source.or(path_sources.get(name)).unwrap_or(default_source);
124+
PackageId::new(name, version, source)
125+
}
126+
127+
81128
#[derive(RustcEncodable, RustcDecodable, Debug, PartialOrd, Ord, PartialEq, Eq)]
82129
pub struct EncodableDependency {
83130
name: String,
@@ -86,15 +133,6 @@ pub struct EncodableDependency {
86133
dependencies: Option<Vec<EncodablePackageId>>
87134
}
88135

89-
impl EncodableDependency {
90-
fn to_package_id(&self, default_source: &SourceId) -> CargoResult<PackageId> {
91-
PackageId::new(
92-
&self.name,
93-
&self.version,
94-
self.source.as_ref().unwrap_or(default_source))
95-
}
96-
}
97-
98136
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
99137
pub struct EncodablePackageId {
100138
name: String,
@@ -134,15 +172,6 @@ impl Decodable for EncodablePackageId {
134172
}
135173
}
136174

137-
impl EncodablePackageId {
138-
fn to_package_id(&self, default_source: &SourceId) -> CargoResult<PackageId> {
139-
PackageId::new(
140-
&self.name,
141-
&self.version,
142-
self.source.as_ref().unwrap_or(default_source))
143-
}
144-
}
145-
146175
impl Encodable for Resolve {
147176
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
148177
let mut ids: Vec<&PackageId> = self.graph.iter().collect();
@@ -151,28 +180,26 @@ impl Encodable for Resolve {
151180
let encodable = ids.iter().filter_map(|&id| {
152181
if self.root == *id { return None; }
153182

154-
Some(encodable_resolve_node(id, &self.root, &self.graph))
183+
Some(encodable_resolve_node(id, &self.graph))
155184
}).collect::<Vec<EncodableDependency>>();
156185

157186
EncodableResolve {
158187
package: Some(encodable),
159-
root: encodable_resolve_node(&self.root, &self.root, &self.graph),
188+
root: encodable_resolve_node(&self.root, &self.graph),
160189
metadata: self.metadata.clone(),
161190
}.encode(s)
162191
}
163192
}
164193

165-
fn encodable_resolve_node(id: &PackageId, root: &PackageId,
166-
graph: &Graph<PackageId>) -> EncodableDependency {
194+
fn encodable_resolve_node(id: &PackageId, graph: &Graph<PackageId>)
195+
-> EncodableDependency {
167196
let deps = graph.edges(id).map(|edge| {
168-
let mut deps = edge.map(|e| {
169-
encodable_package_id(e, root)
170-
}).collect::<Vec<EncodablePackageId>>();
197+
let mut deps = edge.map(encodable_package_id).collect::<Vec<_>>();
171198
deps.sort();
172199
deps
173200
});
174201

175-
let source = if id.source_id() == root.source_id() {
202+
let source = if id.source_id().is_path() {
176203
None
177204
} else {
178205
Some(id.source_id().clone())
@@ -186,8 +213,8 @@ fn encodable_resolve_node(id: &PackageId, root: &PackageId,
186213
}
187214
}
188215

189-
fn encodable_package_id(id: &PackageId, root: &PackageId) -> EncodablePackageId {
190-
let source = if id.source_id() == root.source_id() {
216+
fn encodable_package_id(id: &PackageId) -> EncodablePackageId {
217+
let source = if id.source_id().is_path() {
191218
None
192219
} else {
193220
Some(id.source_id().with_precise(None))

src/cargo/ops/cargo_compile.rs

+20-19
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ use core::{Source, SourceId, PackageSet, Package, Target};
3232
use core::{Profile, TargetKind, Profiles};
3333
use core::resolver::{Method, Resolve};
3434
use ops::{self, BuildOutput, ExecEngine};
35+
use sources::PathSource;
3536
use util::config::Config;
36-
use util::{CargoResult, internal, ChainError, profile};
37+
use util::{CargoResult, profile};
3738

3839
/// Contains information about how a package should be compiled.
3940
pub struct CompileOptions<'a> {
@@ -104,8 +105,6 @@ pub fn resolve_dependencies<'a>(root_package: &Package,
104105
no_default_features: bool)
105106
-> CargoResult<(PackageSet<'a>, Resolve)> {
106107

107-
let override_ids = try!(source_ids_from_config(config, root_package.root()));
108-
109108
let mut registry = PackageRegistry::new(config);
110109

111110
if let Some(source) = source {
@@ -114,14 +113,14 @@ pub fn resolve_dependencies<'a>(root_package: &Package,
114113

115114
// First, resolve the root_package's *listed* dependencies, as well as
116115
// downloading and updating all remotes and such.
117-
let resolve = try!(ops::resolve_pkg(&mut registry, root_package));
116+
let resolve = try!(ops::resolve_pkg(&mut registry, root_package, config));
118117

119118
// Second, resolve with precisely what we're doing. Filter out
120119
// transitive dependencies if necessary, specify features, handle
121120
// overrides, etc.
122121
let _p = profile::start("resolving w/ overrides...");
123122

124-
try!(registry.add_overrides(override_ids));
123+
try!(add_overrides(&mut registry, root_package.root(), config));
125124

126125
let method = Method::Required{
127126
dev_deps: true, // TODO: remove this option?
@@ -383,20 +382,14 @@ fn generate_targets<'a>(pkg: &'a Package,
383382

384383
/// Read the `paths` configuration variable to discover all path overrides that
385384
/// have been configured.
386-
fn source_ids_from_config(config: &Config, cur_path: &Path)
387-
-> CargoResult<Vec<SourceId>> {
388-
389-
let configs = try!(config.values());
390-
debug!("loaded config; configs={:?}", configs);
391-
let config_paths = match configs.get("paths") {
392-
Some(cfg) => cfg,
393-
None => return Ok(Vec::new())
385+
fn add_overrides<'a>(registry: &mut PackageRegistry<'a>,
386+
cur_path: &Path,
387+
config: &'a Config) -> CargoResult<()> {
388+
let paths = match try!(config.get_list("paths")) {
389+
Some(list) => list,
390+
None => return Ok(())
394391
};
395-
let paths = try!(config_paths.list().chain_error(|| {
396-
internal("invalid configuration for the key `paths`")
397-
}));
398-
399-
paths.iter().map(|&(ref s, ref p)| {
392+
let paths = paths.val.iter().map(|&(ref s, ref p)| {
400393
// The path listed next to the string is the config file in which the
401394
// key was located, so we want to pop off the `.cargo/config` component
402395
// to get the directory containing the `.cargo` folder.
@@ -405,7 +398,15 @@ fn source_ids_from_config(config: &Config, cur_path: &Path)
405398
// Make sure we don't override the local package, even if it's in the
406399
// list of override paths.
407400
cur_path != &**p
408-
}).map(|p| SourceId::for_path(&p)).collect()
401+
});
402+
403+
for path in paths {
404+
let id = try!(SourceId::for_path(&path));
405+
let mut source = PathSource::new_recursive(&path, &id, config);
406+
try!(source.update());
407+
registry.add_override(&id, Box::new(source));
408+
}
409+
Ok(())
409410
}
410411

411412
/// Parse all config files to learn about build configuration. Currently

src/cargo/ops/cargo_fetch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub fn fetch<'a>(manifest_path: &Path,
1111
-> CargoResult<(Resolve, PackageSet<'a>)> {
1212
let package = try!(Package::for_path(manifest_path, config));
1313
let mut registry = PackageRegistry::new(config);
14-
let resolve = try!(ops::resolve_pkg(&mut registry, &package));
14+
let resolve = try!(ops::resolve_pkg(&mut registry, &package, config));
1515
let packages = get_resolved_packages(&resolve, registry);
1616
for id in resolve.iter() {
1717
try!(packages.get(id));

0 commit comments

Comments
 (0)