Auto merge of #3611 - tylerwhall:stable-metadata, r=alexcrichton
authorbors <bors@rust-lang.org>
Wed, 9 Aug 2017 14:10:12 +0000 (14:10 +0000)
committerbors <bors@rust-lang.org>
Wed, 9 Aug 2017 14:10:12 +0000 (14:10 +0000)
Stable metadata hashes across workspaces

Currently a crate from a path source will have its metadata hash incorporate its absolute path on the system where it is built. This always impacts top-level crates, which means that compiling the same source with the same dependencies and compiler version will generate libraries with symbol names that vary depending on where the workspace resides on the machine.

This is hopefully a general solution to the hack we've used in meta-rust to make dynamic linking reliable.
meta-rust/meta-rust@0e6cf94

For paths inside the Cargo workspace, hash their SourceId relative to the root of the workspace. Paths outside of a workspace are still hashed as absolute.

This stability is important for reproducible builds as part of a larger build system that employs caching of artifacts, such as OpenEmbedded.

OpenEmbedded tightly controls all inputs to a build and its caching assumes that an equivalent artifact will always result from the same set of inputs. The workspace path is not considered to be an influential input, however.

For example, if Cargo is used to compile libstd shared objects which downstream crates link to dynamically, it must be possible to rebuild libstd given the same inputs and produce a library that is at least link-compatible with the original. If the build system happens to cache the downstream crates but needs to rebuild libstd and the user happens to be building in a different workspace path, currently Cargo will generate a library incompatible with the original and the downstream executables will fail at runtime on the target.

1  2 
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/mod.rs
tests/build.rs
tests/path.rs

index bec1fd930a9d90a7ce4bfdea353ce2c83cd2cc36,b42247293a43ef6e8392d817b016d11d01d96dbf..5b67830e7073761543270f2c199ccaaddaac4323
mode 100755,100644..100755
Simple merge
diff --cc tests/build.rs
index 2f82def40eeb2962ee87d8b89ff598ffa63fcf90,f096b8fbd1e883b90dfc8f32de1ffec1565d2954..a673c1fe7a8c98154d553cebbeb575fd2289e9f8
@@@ -3449,73 -3270,34 +3450,102 @@@ fn no_bin_in_src_with_lib() 
  
      assert_that(p.cargo_process("build"),
                  execs().with_status(101)
 -                       .with_stderr_contains(r#"[ERROR] couldn't read "[..]main.rs"[..]"#));
 +                       .with_stderr_contains("\
 +[ERROR] failed to parse manifest at `[..]`
 +
 +Caused by:
 +  can't find `foo` bin, specify bin.path"));
 +}
 +
 +
 +#[test]
 +fn dirs_in_bin_dir_with_main_rs() {
 +    let p = project("foo")
 +        .file("Cargo.toml", r#"
 +            [package]
 +            name = "foo"
 +            version = "0.1.0"
 +            authors = []
 +        "#)
 +        .file("src/main.rs", "fn main() {}")
 +        .file("src/bin/bar.rs", "fn main() {}")
 +        .file("src/bin/bar2.rs", "fn main() {}")
 +        .file("src/bin/bar3/main.rs", "fn main() {}")
 +        .file("src/bin/bar4/main.rs", "fn main() {}");
 +
 +    assert_that(p.cargo_process("build"), execs().with_status(0));
 +    assert_that(&p.bin("foo"), existing_file());
 +    assert_that(&p.bin("bar"), existing_file());
 +    assert_that(&p.bin("bar2"), existing_file());
 +    assert_that(&p.bin("bar3"), existing_file());
 +    assert_that(&p.bin("bar4"), existing_file());
 +}
 +
 +#[test]
 +fn dir_and_file_with_same_name_in_bin() {
 +    // this should fail, because we have two binaries with the same name
 +    let p = project("bar")
 +        .file("Cargo.toml", r#"
 +            [package]
 +            name = "bar"
 +            version = "0.1.0"
 +            authors = []
 +        "#)
 +        .file("src/main.rs", "fn main() {}")
 +        .file("src/bin/foo.rs", "fn main() {}")
 +        .file("src/bin/foo/main.rs", "fn main() {}");
 +
 +    assert_that(p.cargo_process("build"), 
 +                execs().with_status(101)
 +                       .with_stderr_contains("\
 +[..]found duplicate binary name foo, but all binary targets must have a unique name[..]
 +"));
 +}
 +
 +#[test]
 +fn inferred_path_in_src_bin_foo() {
 +    let p = project("foo")
 +        .file("Cargo.toml", r#"
 +        [package]
 +        name = "foo"
 +        version = "0.1.0"
 +        authors = []
 +
 +        [[bin]]
 +        name = "bar"
 +        # Note, no `path` key!
 +        "#)
 +        .file("src/bin/bar/main.rs", "fn main() {}");
 +
 +    assert_that(p.cargo_process("build"), execs().with_status(0));
 +    assert_that(&p.bin("bar"), existing_file());
  }
+ #[test]
+ fn same_metadata_different_directory() {
+     // A top-level crate built in two different workspaces should have the
+     // same metadata hash.
+     let p = project("foo1")
+         .file("Cargo.toml", &basic_bin_manifest("foo"))
+         .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));
+     let output = t!(String::from_utf8(
+         t!(p.cargo_process("build").arg("-v").exec_with_output())
+             .stderr,
+     ));
+     let metadata = output
+         .split_whitespace()
+         .filter(|arg| arg.starts_with("metadata="))
+         .next()
+         .unwrap();
+     let p = project("foo2")
+         .file("Cargo.toml", &basic_bin_manifest("foo"))
+         .file("src/foo.rs", &main_file(r#""i am foo""#, &[]));
+     assert_that(
+         p.cargo_process("build").arg("-v"),
+         execs().with_status(0).with_stderr_contains(
+             format!("[..]{}[..]", metadata),
+         ),
+     );
+ }
diff --cc tests/path.rs
Simple merge