]> git.proxmox.com Git - cargo.git/commitdiff
Add a metadata section to the lockfile
authorAlex Crichton <alex@alexcrichton.com>
Wed, 15 Oct 2014 23:18:43 +0000 (16:18 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 22 Oct 2014 19:34:05 +0000 (12:34 -0700)
In terms of future compatibility, Cargo may wish to add more information to
lockfiles in the future. For example, the minimum required Rust version may one
day be encoded into a lockfile.

One of the major goals of a lockfile is for it to rarely change, and when it
does change the diffs should be minimal. In terms of adding more information to
the lockfile in the future, this presents a problem for older Cargo clients:

1. Cargo 2.0 adds a minimum required version of rust under the metadata.rustc
   key of the lockfile.
2. Cargo 1.0 is then used to update the `foo` dependency in the lockfile. When
   regenerating the lockfile, Cargo 1.0 discards the metadata inserted by Cargo
   2.0.

In order to future-proof ourselves in allowing new metadata in the future, Cargo
is growing support now for transporting an opaque payload of metadata from one
version of a lockfile to a new versions. This should solve the problem presented
above.

This metadata section is designed to be safely ignored by older Cargo versions
(may just have some surprising behavior), while still allowing newer versions of
Cargo to process the data. For example Cargo 2.0 would gracefully fail on an
out-of-date rustc, while Cargo 1.0 may just present an obscure error message.

src/cargo/core/resolver/encode.rs
src/cargo/core/resolver/mod.rs
src/cargo/ops/cargo_fetch.rs
src/cargo/ops/cargo_generate_lockfile.rs
tests/test_cargo_generate_lockfile.rs

index 2eab82fe3c1bf226948f40ee69164ad09b821b3a..ec684d8725015cc22aa7cc28719faf9f3553cd72 100644 (file)
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use std::collections::{HashMap, TreeMap};
 
 use regex::Regex;
 use serialize::{Encodable, Encoder, Decodable, Decoder};
@@ -11,9 +11,12 @@ use super::Resolve;
 #[deriving(Encodable, Decodable, Show)]
 pub struct EncodableResolve {
     package: Option<Vec<EncodableDependency>>,
-    root: EncodableDependency
+    root: EncodableDependency,
+    metadata: Option<Metadata>,
 }
 
+pub type Metadata = TreeMap<String, String>;
+
 impl EncodableResolve {
     pub fn to_resolve(&self, default: &SourceId) -> CargoResult<Resolve> {
         let mut g = Graph::new();
@@ -30,7 +33,12 @@ impl EncodableResolve {
         }
 
         let root = self.root.to_package_id(default);
-        Ok(Resolve { graph: g, root: try!(root), features: HashMap::new() })
+        Ok(Resolve {
+            graph: g,
+            root: try!(root),
+            features: HashMap::new(),
+            metadata: self.metadata.clone(),
+        })
     }
 }
 
@@ -136,7 +144,8 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Resolve {
 
         EncodableResolve {
             package: Some(encodable),
-            root: encodable_resolve_node(&self.root, &self.root, &self.graph)
+            root: encodable_resolve_node(&self.root, &self.root, &self.graph),
+            metadata: self.metadata.clone(),
         }.encode(s)
     }
 }
index 8909d1d82bd9d82b6023ef311544d8ebaecbff25..5179fc91ef46517d1bc7c68b942f0a7c6f566e9b 100644 (file)
@@ -11,6 +11,7 @@ use util::profile;
 use util::graph::{Nodes, Edges};
 
 pub use self::encode::{EncodableResolve, EncodableDependency, EncodablePackageId};
+pub use self::encode::Metadata;
 
 mod encode;
 
@@ -23,7 +24,8 @@ mod encode;
 pub struct Resolve {
     graph: Graph<PackageId>,
     features: HashMap<PackageId, HashSet<String>>,
-    root: PackageId
+    root: PackageId,
+    metadata: Option<Metadata>,
 }
 
 pub enum ResolveMethod<'a> {
@@ -37,7 +39,11 @@ impl Resolve {
     fn new(root: PackageId) -> Resolve {
         let mut g = Graph::new();
         g.add(root.clone(), []);
-        Resolve { graph: g, root: root, features: HashMap::new() }
+        Resolve { graph: g, root: root, features: HashMap::new(), metadata: None }
+    }
+
+    pub fn copy_metadata(&mut self, other: &Resolve) {
+        self.metadata = other.metadata.clone();
     }
 
     pub fn iter(&self) -> Nodes<PackageId> {
index e8820df8b0f003398cbb6ec93b6c241f4bb2c052..3f04af2951c013b82c63dd108021b95b2fa6bc09 100644 (file)
@@ -32,14 +32,19 @@ pub fn resolve_and_fetch(registry: &mut PackageRegistry, package: &Package)
 
     let lockfile = package.get_manifest_path().dir_path().join("Cargo.lock");
     let source_id = package.get_package_id().get_source_id();
-    match try!(ops::load_lockfile(&lockfile, source_id)) {
-        Some(r) => try!(add_lockfile_sources(registry, package, &r)),
+    let previous_resolve = try!(ops::load_lockfile(&lockfile, source_id));
+    match previous_resolve {
+        Some(ref r) => try!(add_lockfile_sources(registry, package, r)),
         None => try!(registry.add_sources(package.get_source_ids())),
     }
 
-    let resolved = try!(resolver::resolve(package.get_summary(),
-                                          resolver::ResolveEverything,
-                                          registry));
+    let mut resolved = try!(resolver::resolve(package.get_summary(),
+                                              resolver::ResolveEverything,
+                                              registry));
+    match previous_resolve {
+        Some(ref prev) => resolved.copy_metadata(prev),
+        None => {}
+    }
     try!(ops::write_resolve(package, &resolved));
     Ok(resolved)
 }
index 533caed5c9d2f24ea3c96366df586e21476cc2c7..43be2d6e8206b45b26c50645870bfa30182397f0 100644 (file)
@@ -47,7 +47,7 @@ pub fn update_lockfile(manifest_path: &Path,
 
     let lockfile = package.get_root().join("Cargo.lock");
     let source_id = package.get_package_id().get_source_id();
-    let resolve = match try!(load_lockfile(&lockfile, source_id)) {
+    let previous_resolve = match try!(load_lockfile(&lockfile, source_id)) {
         Some(resolve) => resolve,
         None => return Err(human("A Cargo.lock must exist before it is updated"))
     };
@@ -64,10 +64,10 @@ pub fn update_lockfile(manifest_path: &Path,
     match opts.to_update {
         Some(name) => {
             let mut to_avoid = HashSet::new();
-            let dep = try!(resolve.query(name));
+            let dep = try!(previous_resolve.query(name));
             if opts.aggressive {
-                let mut visited = HashSet::new();
-                fill_with_deps(&resolve, dep, &mut to_avoid, &mut visited);
+                fill_with_deps(&previous_resolve, dep, &mut to_avoid,
+                               &mut HashSet::new());
             } else {
                 to_avoid.insert(dep.get_source_id());
                 match opts.precise {
@@ -78,7 +78,7 @@ pub fn update_lockfile(manifest_path: &Path,
                     None => {}
                 }
             }
-            sources.extend(resolve.iter()
+            sources.extend(previous_resolve.iter()
                                   .map(|p| p.get_source_id())
                                   .filter(|s| !to_avoid.contains(s))
                                   .map(|s| s.clone()));
@@ -87,10 +87,10 @@ pub fn update_lockfile(manifest_path: &Path,
     }
     try!(registry.add_sources(sources));
 
-    let resolve = try!(resolver::resolve(package.get_summary(),
-                                         resolver::ResolveEverything,
-                                         &mut registry));
-
+    let mut resolve = try!(resolver::resolve(package.get_summary(),
+                                             resolver::ResolveEverything,
+                                             &mut registry));
+    resolve.copy_metadata(&previous_resolve);
     try!(write_resolve(&package, &resolve));
     return Ok(());
 
@@ -149,6 +149,14 @@ pub fn write_resolve(pkg: &Package, resolve: &Resolve) -> CargoResult<()> {
         emit_package(dep, &mut out);
     }
 
+    match e.toml.find(&"metadata".to_string()) {
+        Some(metadata) => {
+            out.push_str("[metadata]\n");
+            out.push_str(metadata.to_string().as_slice());
+        }
+        None => {}
+    }
+
     try!(File::create(&loc).write_str(out.as_slice()));
     Ok(())
 }
index 5164ea2bc8e787475dd869d6a0ae7b994a01d014..9dd5c19bdd6824d7751896f3289fa152e955b068 100644 (file)
@@ -94,3 +94,47 @@ test!(adding_and_removing_packages {
     let lock4 = File::open(&lockfile).read_to_string().assert();
     assert_eq!(lock1, lock4);
 })
+
+test!(preserve_metadata {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            authors = []
+            version = "0.0.1"
+        "#)
+        .file("src/main.rs", "fn main() {}")
+        .file("bar/Cargo.toml", r#"
+            [package]
+            name = "bar"
+            authors = []
+            version = "0.0.1"
+        "#)
+        .file("bar/src/lib.rs", "");
+
+    assert_that(p.cargo_process("generate-lockfile"),
+                execs().with_status(0));
+
+    let metadata = r#"
+[metadata]
+bar = "baz"
+foo = "bar"
+"#;
+    let lockfile = p.root().join("Cargo.lock");
+    {
+        let lock = File::open(&lockfile).read_to_string().assert();
+        File::create(&lockfile).write_str((lock + metadata).as_slice()).assert();
+    }
+
+    // Build and make sure the metadata is still there
+    assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
+                execs().with_status(0));
+    let lock = File::open(&lockfile).read_to_string().assert();
+    assert!(lock.as_slice().contains(metadata.trim()), "{}", lock);
+
+    // Update and make sure the metadata is still there
+    assert_that(p.process(cargo_dir().join("cargo")).arg("update"),
+                execs().with_status(0));
+    let lock = File::open(&lockfile).read_to_string().assert();
+    assert!(lock.as_slice().contains(metadata.trim()), "{}", lock);
+})