Hashed dependencies of metadata into the metadata of a lib
authorNipunn Koorapati <nipunn@dropbox.com>
Tue, 5 Sep 2017 00:13:26 +0000 (17:13 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Sat, 9 Sep 2017 20:46:02 +0000 (13:46 -0700)
src/cargo/ops/cargo_rustc/context.rs
tests/workspaces.rs

index 906a5155a9335980286eb9e1f25c26ee298506b2..ffce9680b9f19a92fead6ffd94db4b6f8d6127df 100755 (executable)
@@ -82,7 +82,7 @@ impl TargetInfo {
     }
 }
 
-#[derive(Clone)]
+#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
 pub struct Metadata(u64);
 
 impl<'a, 'cfg> Context<'a, 'cfg> {
@@ -483,6 +483,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         // when changing feature sets each lib is separately cached.
         self.resolve.features_sorted(unit.pkg.package_id()).hash(&mut hasher);
 
+        if let Ok(dep_units) =  self.dep_targets(&unit) {
+            let mut dep_metadatas = dep_units.into_iter().map(|dep_unit| {
+                self.target_metadata(&dep_unit)
+            }).collect::<Vec<_>>();
+            dep_metadatas.sort();
+            for metadata in dep_metadatas {
+                metadata.hash(&mut hasher);
+            }
+        }
+
         // Throw in the profile we're compiling with. This helps caching
         // panic=abort and panic=unwind artifacts, additionally with various
         // settings like debuginfo and whatnot.
index 1228ae2821844af39d22063c77c22be55a0c3231..215af77ec0bbbabeeb1b2b7afd8b501877e922be 100644 (file)
@@ -1465,3 +1465,94 @@ Caused by:
 "));
 }
 
+/// This is a freshness test for feature use with workspaces
+///
+/// feat_lib is used by caller1 and caller2, but with different features enabled.
+/// This test ensures that alternating building caller1, caller2 doesn't force
+/// recompile of feat_lib.
+///
+/// Ideally once we solve https://github.com/rust-lang/cargo/issues/3620, then
+/// a single cargo build at the top level will be enough.
+#[test]
+fn dep_used_with_separate_features() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [workspace]
+            members = ["feat_lib", "caller1", "caller2"]
+        "#)
+        .file("feat_lib/Cargo.toml", r#"
+            [project]
+            name = "feat_lib"
+            version = "0.1.0"
+            authors = []
+
+            [features]
+            myfeature = []
+        "#)
+        .file("feat_lib/src/lib.rs", "")
+        .file("caller1/Cargo.toml", r#"
+            [project]
+            name = "caller1"
+            version = "0.1.0"
+            authors = []
+
+            [dependencies]
+            feat_lib = { path = "../feat_lib" }
+        "#)
+        .file("caller1/src/main.rs", "fn main() {}")
+        .file("caller1/src/lib.rs", "")
+        .file("caller2/Cargo.toml", r#"
+            [project]
+            name = "caller2"
+            version = "0.1.0"
+            authors = []
+
+            [dependencies]
+            feat_lib = { path = "../feat_lib", features = ["myfeature"] }
+            caller1 = { path = "../caller1" }
+        "#)
+        .file("caller2/src/main.rs", "fn main() {}")
+        .file("caller2/src/lib.rs", "");
+    p.build();
+
+    // Build the entire workspace
+    assert_that(p.cargo("build"),
+                execs().with_status(0)
+                       .with_stderr("\
+[..]Compiling feat_lib v0.1.0 ([..])
+[..]Compiling caller1 v0.1.0 ([..])
+[..]Compiling caller2 v0.1.0 ([..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+"));
+    assert_that(&p.bin("caller1"), existing_file());
+    assert_that(&p.bin("caller2"), existing_file());
+
+
+    // Build caller1. should build the dep library. Because the features
+    // are different than the full workspace, it rebuilds.
+    assert_that(p.cargo("build").cwd(p.root().join("caller1")),
+                execs().with_status(0)
+                       .with_stderr("\
+[..]Compiling feat_lib v0.1.0 ([..])
+[..]Compiling caller1 v0.1.0 ([..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+"));
+
+    // Alternate building caller2/caller1 a few times, just to make sure
+    // features are being built separately.  Should not rebuild anything
+    assert_that(p.cargo("build").cwd(p.root().join("caller2")),
+                execs().with_status(0)
+                       .with_stderr("\
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+"));
+    assert_that(p.cargo("build").cwd(p.root().join("caller1")),
+                execs().with_status(0)
+                       .with_stderr("\
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+"));
+    assert_that(p.cargo("build").cwd(p.root().join("caller2")),
+                execs().with_status(0)
+                       .with_stderr("\
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+"));
+}