blob: f53f002ecb48e6d8a01dbd715ed75ba675ae678e [file] [log] [blame]
//! Tests for dep-info files. This includes the dep-info file Cargo creates in
//! the output directory, and the ones stored in the fingerprint.
use cargo_test_support::compare::assert_match_exact;
use cargo_test_support::paths::{self, CargoPathExt};
use cargo_test_support::registry::Package;
use cargo_test_support::{
basic_bin_manifest, basic_manifest, main_file, project, rustc_host, Project,
};
use filetime::FileTime;
use std::fs;
use std::path::Path;
use std::str;
// Helper for testing dep-info files in the fingerprint dir.
#[track_caller]
fn assert_deps(project: &Project, fingerprint: &str, test_cb: impl Fn(&Path, &[(u8, &str)])) {
let mut files = project
.glob(fingerprint)
.map(|f| f.expect("unwrap glob result"))
// Filter out `.json` entries.
.filter(|f| f.extension().is_none());
let info_path = files
.next()
.unwrap_or_else(|| panic!("expected 1 dep-info file at {}, found 0", fingerprint));
assert!(files.next().is_none(), "expected only 1 dep-info file");
let dep_info = fs::read(&info_path).unwrap();
let dep_info = &mut &dep_info[..];
let deps = (0..read_usize(dep_info))
.map(|_| {
(
read_u8(dep_info),
str::from_utf8(read_bytes(dep_info)).unwrap(),
)
})
.collect::<Vec<_>>();
test_cb(&info_path, &deps);
fn read_usize(bytes: &mut &[u8]) -> usize {
let ret = &bytes[..4];
*bytes = &bytes[4..];
u32::from_le_bytes(ret.try_into().unwrap()) as usize
}
fn read_u8(bytes: &mut &[u8]) -> u8 {
let ret = bytes[0];
*bytes = &bytes[1..];
ret
}
fn read_bytes<'a>(bytes: &mut &'a [u8]) -> &'a [u8] {
let n = read_usize(bytes);
let ret = &bytes[..n];
*bytes = &bytes[n..];
ret
}
}
fn assert_deps_contains(project: &Project, fingerprint: &str, expected: &[(u8, &str)]) {
assert_deps(project, fingerprint, |info_path, entries| {
for (e_kind, e_path) in expected {
let pattern = glob::Pattern::new(e_path).unwrap();
let count = entries
.iter()
.filter(|(kind, path)| kind == e_kind && pattern.matches(path))
.count();
if count != 1 {
panic!(
"Expected 1 match of {} {} in {:?}, got {}:\n{:#?}",
e_kind, e_path, info_path, count, entries
);
}
}
})
}
#[cargo_test]
fn build_dep_info() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
.file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
.build();
p.cargo("build").run();
let depinfo_bin_path = &p.bin("foo").with_extension("d");
assert!(depinfo_bin_path.is_file());
let depinfo = p.read_file(depinfo_bin_path.to_str().unwrap());
let bin_path = p.bin("foo");
let src_path = p.root().join("src").join("foo.rs");
if !depinfo.lines().any(|line| {
line.starts_with(&format!("{}:", bin_path.display()))
&& line.contains(src_path.to_str().unwrap())
}) {
panic!(
"Could not find {:?}: {:?} in {:?}",
bin_path, src_path, depinfo_bin_path
);
}
}
#[cargo_test]
fn build_dep_info_lib() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[[example]]
name = "ex"
crate-type = ["lib"]
"#,
)
.file("build.rs", "fn main() {}")
.file("src/lib.rs", "")
.file("examples/ex.rs", "")
.build();
p.cargo("build --example=ex").run();
assert!(p.example_lib("ex", "lib").with_extension("d").is_file());
}
#[cargo_test]
fn build_dep_info_rlib() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[[example]]
name = "ex"
crate-type = ["rlib"]
"#,
)
.file("src/lib.rs", "")
.file("examples/ex.rs", "")
.build();
p.cargo("build --example=ex").run();
assert!(p.example_lib("ex", "rlib").with_extension("d").is_file());
}
#[cargo_test]
fn build_dep_info_dylib() {
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[[example]]
name = "ex"
crate-type = ["dylib"]
"#,
)
.file("src/lib.rs", "")
.file("examples/ex.rs", "")
.build();
p.cargo("build --example=ex").run();
assert!(p.example_lib("ex", "dylib").with_extension("d").is_file());
}
#[cargo_test]
fn dep_path_inside_target_has_correct_path() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("a"))
.file("target/debug/blah", "")
.file(
"src/main.rs",
r#"
fn main() {
let x = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/target/debug/blah"));
}
"#,
)
.build();
p.cargo("build").run();
let depinfo_path = &p.bin("a").with_extension("d");
assert!(depinfo_path.is_file(), "{:?}", depinfo_path);
let depinfo = p.read_file(depinfo_path.to_str().unwrap());
let bin_path = p.bin("a");
let target_debug_blah = Path::new("target").join("debug").join("blah");
if !depinfo.lines().any(|line| {
line.starts_with(&format!("{}:", bin_path.display()))
&& line.contains(target_debug_blah.to_str().unwrap())
}) {
panic!(
"Could not find {:?}: {:?} in {:?}",
bin_path, target_debug_blah, depinfo_path
);
}
}
#[cargo_test]
fn no_rewrite_if_no_change() {
let p = project().file("src/lib.rs", "").build();
p.cargo("build").run();
let dep_info = p.root().join("target/debug/libfoo.d");
let metadata1 = dep_info.metadata().unwrap();
p.cargo("build").run();
let metadata2 = dep_info.metadata().unwrap();
assert_eq!(
FileTime::from_last_modification_time(&metadata1),
FileTime::from_last_modification_time(&metadata2),
);
}
#[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")]
fn relative_depinfo_paths_ws() {
// Test relative dep-info paths in a workspace with --target with
// proc-macros and other dependency kinds.
Package::new("regdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
Package::new("pmdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
Package::new("bdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
let p = project()
/*********** Workspace ***********/
.file(
"Cargo.toml",
r#"
[workspace]
members = ["foo"]
"#,
)
/*********** Main Project ***********/
.file(
"foo/Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[dependencies]
pm = {path = "../pm"}
bar = {path = "../bar"}
regdep = "0.1"
[build-dependencies]
bdep = "0.1"
bar = {path = "../bar"}
"#,
)
.file(
"foo/src/main.rs",
r#"
pm::noop!{}
fn main() {
bar::f();
regdep::f();
}
"#,
)
.file("foo/build.rs", "fn main() { bdep::f(); }")
/*********** Proc Macro ***********/
.file(
"pm/Cargo.toml",
r#"
[package]
name = "pm"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
pmdep = "0.1"
"#,
)
.file(
"pm/src/lib.rs",
r#"
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn noop(_item: TokenStream) -> TokenStream {
pmdep::f();
"".parse().unwrap()
}
"#,
)
/*********** Path Dependency `bar` ***********/
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn f() {}")
.build();
let host = rustc_host();
p.cargo("build -Z binary-dep-depinfo --target")
.arg(&host)
.masquerade_as_nightly_cargo(&["binary-dep-depinfo"])
.with_stderr_contains("[COMPILING] foo [..]")
.run();
assert_deps_contains(
&p,
"target/debug/.fingerprint/pm-*/dep-lib-pm",
&[(0, "src/lib.rs"), (1, "debug/deps/libpmdep-*.rlib")],
);
assert_deps_contains(
&p,
&format!("target/{}/debug/.fingerprint/foo-*/dep-bin-foo", host),
&[
(0, "src/main.rs"),
(
1,
&format!(
"debug/deps/{}pm-*.{}",
paths::get_lib_prefix("proc-macro"),
paths::get_lib_extension("proc-macro")
),
),
(1, &format!("{}/debug/deps/libbar-*.rlib", host)),
(1, &format!("{}/debug/deps/libregdep-*.rlib", host)),
],
);
assert_deps_contains(
&p,
"target/debug/.fingerprint/foo-*/dep-build-script-build-script-build",
&[(0, "build.rs"), (1, "debug/deps/libbdep-*.rlib")],
);
// Make sure it stays fresh.
p.cargo("build -Z binary-dep-depinfo --target")
.arg(&host)
.masquerade_as_nightly_cargo(&["binary-dep-depinfo"])
.with_stderr("[FINISHED] `dev` profile [..]")
.run();
}
#[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")]
fn relative_depinfo_paths_no_ws() {
// Test relative dep-info paths without a workspace with proc-macros and
// other dependency kinds.
Package::new("regdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
Package::new("pmdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
Package::new("bdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
let p = project()
/*********** Main Project ***********/
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
edition = "2018"
[dependencies]
pm = {path = "pm"}
bar = {path = "bar"}
regdep = "0.1"
[build-dependencies]
bdep = "0.1"
bar = {path = "bar"}
"#,
)
.file(
"src/main.rs",
r#"
pm::noop!{}
fn main() {
bar::f();
regdep::f();
}
"#,
)
.file("build.rs", "fn main() { bdep::f(); }")
/*********** Proc Macro ***********/
.file(
"pm/Cargo.toml",
r#"
[package]
name = "pm"
version = "0.1.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
pmdep = "0.1"
"#,
)
.file(
"pm/src/lib.rs",
r#"
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn noop(_item: TokenStream) -> TokenStream {
pmdep::f();
"".parse().unwrap()
}
"#,
)
/*********** Path Dependency `bar` ***********/
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
.file("bar/src/lib.rs", "pub fn f() {}")
.build();
p.cargo("build -Z binary-dep-depinfo")
.masquerade_as_nightly_cargo(&["binary-dep-depinfo"])
.with_stderr_contains("[COMPILING] foo [..]")
.run();
assert_deps_contains(
&p,
"target/debug/.fingerprint/pm-*/dep-lib-pm",
&[(0, "src/lib.rs"), (1, "debug/deps/libpmdep-*.rlib")],
);
assert_deps_contains(
&p,
"target/debug/.fingerprint/foo-*/dep-bin-foo",
&[
(0, "src/main.rs"),
(
1,
&format!(
"debug/deps/{}pm-*.{}",
paths::get_lib_prefix("proc-macro"),
paths::get_lib_extension("proc-macro")
),
),
(1, "debug/deps/libbar-*.rlib"),
(1, "debug/deps/libregdep-*.rlib"),
],
);
assert_deps_contains(
&p,
"target/debug/.fingerprint/foo-*/dep-build-script-build-script-build",
&[(0, "build.rs"), (1, "debug/deps/libbdep-*.rlib")],
);
// Make sure it stays fresh.
p.cargo("build -Z binary-dep-depinfo")
.masquerade_as_nightly_cargo(&["binary-dep-depinfo"])
.with_stderr("[FINISHED] `dev` profile [..]")
.run();
}
#[cargo_test]
fn reg_dep_source_not_tracked() {
// Make sure source files in dep-info file are not tracked for registry dependencies.
Package::new("regdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
regdep = "0.1"
"#,
)
.file("src/lib.rs", "pub fn f() { regdep::f(); }")
.build();
p.cargo("check").run();
assert_deps(
&p,
"target/debug/.fingerprint/regdep-*/dep-lib-regdep",
|info_path, entries| {
for (kind, path) in entries {
if *kind == 1 {
panic!(
"Did not expect package root relative path type: {:?} in {:?}",
path, info_path
);
}
}
},
);
}
#[cargo_test(nightly, reason = "-Z binary-dep-depinfo is unstable")]
fn canonical_path() {
if !cargo_test_support::symlink_supported() {
return;
}
Package::new("regdep", "0.1.0")
.file("src/lib.rs", "pub fn f() {}")
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
regdep = "0.1"
"#,
)
.file("src/lib.rs", "pub fn f() { regdep::f(); }")
.build();
let real = p.root().join("real_target");
real.mkdir_p();
p.symlink(real, "target");
p.cargo("check -Z binary-dep-depinfo")
.masquerade_as_nightly_cargo(&["binary-dep-depinfo"])
.run();
assert_deps_contains(
&p,
"target/debug/.fingerprint/foo-*/dep-lib-foo",
&[(0, "src/lib.rs"), (1, "debug/deps/libregdep-*.rmeta")],
);
}
#[cargo_test]
fn non_local_build_script() {
// Non-local build script information is not included.
Package::new("bar", "1.0.0")
.file(
"build.rs",
r#"
fn main() {
println!("cargo::rerun-if-changed=build.rs");
}
"#,
)
.file("src/lib.rs", "")
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"
[dependencies]
bar = "1.0"
"#,
)
.file("src/main.rs", "fn main() {}")
.build();
p.cargo("build").run();
let contents = p.read_file("target/debug/foo.d");
assert_match_exact(
"[ROOT]/foo/target/debug/foo[EXE]: [ROOT]/foo/src/main.rs",
&contents,
);
}