Skip to content

Be forward compat with rootless lock files #3023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions src/cargo/core/resolver/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use super::Resolve;
#[derive(RustcEncodable, RustcDecodable, Debug)]
pub struct EncodableResolve {
package: Option<Vec<EncodableDependency>>,
root: EncodableDependency,
/// `root` is optional to allow forward compatibility.
root: Option<EncodableDependency>,
metadata: Option<Metadata>,
}

Expand All @@ -28,9 +29,6 @@ impl EncodableResolve {
let mut tmp = HashMap::new();
let mut replacements = HashMap::new();

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

let id2pkgid = |id: &EncodablePackageId| {
to_package_id(&id.name, &id.version, id.source.as_ref(),
default, &path_deps)
Expand All @@ -40,7 +38,14 @@ impl EncodableResolve {
default, &path_deps)
};

let root = try!(dep2pkgid(&self.root));
let packages = {
let mut packages = self.package.unwrap_or(Vec::new());
if let Some(root) = self.root {
packages.insert(0, root);
}
packages
};

let ids = try!(packages.iter().map(&dep2pkgid)
.collect::<CargoResult<Vec<_>>>());

Expand All @@ -56,7 +61,6 @@ impl EncodableResolve {
Ok(())
};

try!(register_pkg(&root));
for id in ids.iter() {
try!(register_pkg(id));
}
Expand Down Expand Up @@ -90,8 +94,7 @@ impl EncodableResolve {
Ok(())
};

try!(add_dependencies(&root, &self.root));
for (id, pkg) in ids.iter().zip(packages) {
for (id, ref pkg) in ids.iter().zip(packages) {
try!(add_dependencies(id, pkg));
}
}
Expand Down Expand Up @@ -268,6 +271,7 @@ impl Decodable for EncodablePackageId {
pub struct WorkspaceResolve<'a, 'cfg: 'a> {
pub ws: &'a Workspace<'cfg>,
pub resolve: &'a Resolve,
pub use_root_key: bool,
}

impl<'a, 'cfg> Encodable for WorkspaceResolve<'a, 'cfg> {
Expand All @@ -280,7 +284,7 @@ impl<'a, 'cfg> Encodable for WorkspaceResolve<'a, 'cfg> {
}).unwrap().package_id();

let encodable = ids.iter().filter_map(|&id| {
if root == id {
if self.use_root_key && root == id {
return None
}

Expand All @@ -300,9 +304,15 @@ impl<'a, 'cfg> Encodable for WorkspaceResolve<'a, 'cfg> {
}

let metadata = if metadata.len() == 0 {None} else {Some(metadata)};

let root = if self.use_root_key {
Some(encodable_resolve_node(&root, self.resolve))
} else {
None
};
EncodableResolve {
package: Some(encodable),
root: encodable_resolve_node(&root, self.resolve),
root: root,
metadata: metadata,
}.encode(s)
}
Expand Down
38 changes: 23 additions & 15 deletions src/cargo/ops/lockfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,39 @@ pub fn load_pkg_lockfile(ws: &Workspace) -> CargoResult<Option<Resolve>> {
}

pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()> {
// Load the original lockfile if it exists.
let ws_root = Filesystem::new(ws.root().to_path_buf());
let orig = ws_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file");
let orig = orig.and_then(|mut f| {
let mut s = String::new();
try!(f.read_to_string(&mut s));
Ok(s)
});

// Forward compatibility: if `orig` uses rootless format
// from the future, do the same.
let use_root_key = if let Ok(ref orig) = orig {
!orig.starts_with("[[package]]")
} else {
true
};

let mut e = Encoder::new();
WorkspaceResolve {
ws: ws,
resolve: resolve,
use_root_key: use_root_key,
}.encode(&mut e).unwrap();

let mut out = String::new();

// Note that we do not use e.toml.to_string() as we want to control the
// exact format the toml is in to ensure pretty diffs between updates to the
// lockfile.
let root = e.toml.get(&"root".to_string()).unwrap();

out.push_str("[root]\n");
emit_package(root.as_table().unwrap(), &mut out);
if let Some(root) = e.toml.get(&"root".to_string()) {
out.push_str("[root]\n");
emit_package(root.as_table().unwrap(), &mut out);
}

let deps = e.toml.get(&"package".to_string()).unwrap().as_slice().unwrap();
for dep in deps.iter() {
Expand All @@ -65,18 +83,8 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()>
None => {}
}

let root = Filesystem::new(ws.root().to_path_buf());

// Load the original lockfile if it exists.
//
// If the lockfile contents haven't changed so don't rewrite it. This is
// helpful on read-only filesystems.
let orig = root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file");
let orig = orig.and_then(|mut f| {
let mut s = String::new();
try!(f.read_to_string(&mut s));
Ok(s)
});
if let Ok(orig) = orig {
if has_crlf_line_endings(&orig) {
out = out.replace("\n", "\r\n");
Expand All @@ -93,7 +101,7 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()>
}

// Ok, if that didn't work just write it out
root.open_rw("Cargo.lock", ws.config(), "Cargo.lock file").and_then(|mut f| {
ws_root.open_rw("Cargo.lock", ws.config(), "Cargo.lock file").and_then(|mut f| {
try!(f.file().set_len(0));
try!(f.write_all(out.as_bytes()));
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions tests/bad-config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,15 @@ fn bad_cargo_lock() {
version = "0.0.0"
authors = []
"#)
.file("Cargo.lock", "")
.file("Cargo.lock", "[[package]]\nfoo = 92")
.file("src/lib.rs", "");

assert_that(foo.cargo_process("build").arg("-v"),
execs().with_status(101).with_stderr("\
[ERROR] failed to parse lock file at: [..]Cargo.lock

Caused by:
expected a section for the key `root`
expected a value of type `string` for the key `package.name`
"));
}

Expand Down
41 changes: 41 additions & 0 deletions tests/lockfile-compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,44 @@ unable to verify that `foo v0.1.0 ([..])` is the same as when the lockfile was g

"));
}

#[test]
fn lockfile_without_root() {
Package::new("foo", "0.1.0").publish();

let p = project("bar")
.file("Cargo.toml", r#"
[package]
name = "bar"
version = "0.0.1"
authors = []

[dependencies]
foo = "0.1.0"
"#)
.file("src/lib.rs", "");
p.build();

let lockfile = r#"[[package]]
name = "bar"
version = "0.0.1"
dependencies = [
"foo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

[[package]]
name = "foo"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
"#;
File::create(p.root().join("Cargo.lock")).unwrap()
.write_all(lockfile.as_bytes()).unwrap();

assert_that(p.cargo("build"),
execs().with_status(0));

let mut lock = String::new();
File::open(p.root().join("Cargo.lock")).unwrap()
.read_to_string(&mut lock).unwrap();
assert!(lock.starts_with(lockfile.trim()));
}