| //! 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 [..]") |
| .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 [..]") |
| .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, |
| ); |
| } |