summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/artifact_dep.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/artifact_dep.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/artifact_dep.rs2901
1 files changed, 2901 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/artifact_dep.rs b/src/tools/cargo/tests/testsuite/artifact_dep.rs
new file mode 100644
index 000000000..ec6bb7103
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/artifact_dep.rs
@@ -0,0 +1,2901 @@
+//! Tests specific to artifact dependencies, designated using
+//! the new `dep = { artifact = "bin", … }` syntax in manifests.
+
+use cargo_test_support::compare::match_exact;
+use cargo_test_support::registry::{Package, RegistryBuilder};
+use cargo_test_support::{
+ basic_bin_manifest, basic_manifest, cross_compile, project, publish, registry, rustc_host,
+ Project,
+};
+
+#[cargo_test]
+fn check_with_invalid_artifact_dependency() {
+ // invalid name
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "unknown" }
+ "#,
+ )
+ .file("src/lib.rs", "extern crate bar;") // this would fail but we don't get there, artifacts are no libs
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]/Cargo.toml`
+
+Caused by:
+ 'unknown' is not a valid artifact specifier
+",
+ )
+ .with_status(101)
+ .run();
+
+ fn run_cargo_with_and_without_bindeps_feature(
+ p: &Project,
+ cmd: &str,
+ assert: &dyn Fn(&mut cargo_test_support::Execs),
+ ) {
+ assert(
+ p.cargo(&format!("{} -Z bindeps", cmd))
+ .masquerade_as_nightly_cargo(&["bindeps"]),
+ );
+ assert(&mut p.cargo(cmd));
+ }
+
+ // lib specified without artifact
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies]
+ bar = { path = "bar/", lib = true }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "")
+ .build();
+ run_cargo_with_and_without_bindeps_feature(&p, "check", &|cargo| {
+ cargo
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]/Cargo.toml`
+
+Caused by:
+ 'lib' specifier cannot be used without an 'artifact = …' value (bar)
+",
+ )
+ .with_status(101)
+ .run();
+ });
+
+ // target specified without artifact
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies]
+ bar = { path = "bar/", target = "target" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "")
+ .build();
+ run_cargo_with_and_without_bindeps_feature(&p, "check", &|cargo| {
+ cargo
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]/Cargo.toml`
+
+Caused by:
+ 'target' specifier cannot be used without an 'artifact = …' value (bar)
+",
+ )
+ .with_status(101)
+ .run();
+ })
+}
+
+#[cargo_test]
+fn check_with_invalid_target_triple() {
+ // invalid name
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin", target = "unknown-target-triple" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains(
+ r#"[..]Could not find specification for target "unknown-target-triple"[..]"#,
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn build_without_nightly_aborts_with_error() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file("src/lib.rs", "extern crate bar;")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "")
+ .build();
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at [..]
+
+Caused by:
+ `artifact = …` requires `-Z bindeps` (bar)
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn disallow_artifact_and_no_artifact_dep_to_same_package_within_the_same_dep_category() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ bar_stable = { path = "bar/", package = "bar" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr("\
+[WARNING] foo v0.0.0 ([CWD]) ignoring invalid dependency `bar_stable` which is missing a lib target
+[ERROR] the crate `foo v0.0.0 ([CWD])` depends on crate `bar v0.5.0 ([CWD]/bar)` multiple times with different names",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn features_are_unified_among_lib_and_bin_dep_of_same_target() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ resolver = "2"
+
+ [dependencies.d1]
+ path = "d1"
+ features = ["d1f1"]
+ artifact = "bin"
+ lib = true
+
+ [dependencies.d2]
+ path = "d2"
+ features = ["d2f2"]
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ d1::f1();
+ d1::f2();
+ d2::f1();
+ d2::f2();
+ }
+ "#,
+ )
+ .file(
+ "d1/Cargo.toml",
+ r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ d1f1 = ["d2"]
+
+ [dependencies.d2]
+ path = "../d2"
+ features = ["d2f1"]
+ optional = true
+ "#,
+ )
+ .file(
+ "d1/src/main.rs",
+ r#"fn main() {
+ #[cfg(feature = "d1f1")]
+ d2::f1();
+
+ // Using f2 is only possible as features are unififed across the same target.
+ // Our own manifest would only enable f1, and f2 comes in because a parent crate
+ // enables the feature in its manifest.
+ #[cfg(feature = "d1f1")]
+ d2::f2();
+ }"#,
+ )
+ .file(
+ "d1/src/lib.rs",
+ r#"
+ #[cfg(feature = "d2")]
+ extern crate d2;
+ /// Importing f2 here shouldn't be possible as unless features are unified.
+ #[cfg(feature = "d1f1")]
+ pub use d2::{f1, f2};
+ "#,
+ )
+ .file(
+ "d2/Cargo.toml",
+ r#"
+ [package]
+ name = "d2"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ d2f1 = []
+ d2f2 = []
+ "#,
+ )
+ .file(
+ "d2/src/lib.rs",
+ r#"
+ #[cfg(feature = "d2f1")] pub fn f1() {}
+ #[cfg(feature = "d2f2")] pub fn f2() {}
+ "#,
+ )
+ .build();
+
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] d2 v0.0.1 ([CWD]/d2)
+[COMPILING] d1 v0.0.1 ([CWD]/d1)
+[COMPILING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn features_are_not_unified_among_lib_and_bin_dep_of_different_target() {
+ if cross_compile::disabled() {
+ return;
+ }
+ let target = cross_compile::alternate();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ resolver = "2"
+
+ [dependencies.d1]
+ path = "d1"
+ features = ["d1f1"]
+ artifact = "bin"
+ lib = true
+ target = "$TARGET"
+
+ [dependencies.d2]
+ path = "d2"
+ features = ["d2f2"]
+ "#
+ .replace("$TARGET", target),
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ // the lib = true part always builds for our current target, unifying dependencies
+ d1::d2::f1();
+ d1::d2::f2();
+ d2::f1();
+ d2::f2();
+ }
+ "#,
+ )
+ .file(
+ "d1/Cargo.toml",
+ r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ d1f1 = ["d2"]
+
+ [dependencies.d2]
+ path = "../d2"
+ features = ["d2f1"]
+ optional = true
+ "#,
+ )
+ .file("d1/src/main.rs", r#"fn main() {
+ // f1 we set ourselves
+ d2::f1();
+ // As 'main' is only compiled as part of the artifact dependency and since that is not unified
+ // if the target differs, trying to access f2 is a compile time error as the feature isn't enabled in our dependency tree.
+ d2::f2();
+ }"#)
+ .file(
+ "d1/src/lib.rs",
+ r#"
+ #[cfg(feature = "d2")]
+ pub extern crate d2;
+ "#,
+ )
+ .file(
+ "d2/Cargo.toml",
+ r#"
+ [package]
+ name = "d2"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ d2f1 = []
+ d2f2 = []
+ "#,
+ )
+ .file(
+ "d2/src/lib.rs",
+ r#"
+ #[cfg(feature = "d2f1")] pub fn f1() {}
+ #[cfg(feature = "d2f2")] pub fn f2() {}
+ "#,
+ )
+ .build();
+
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr_contains(
+ "error[E0425]: cannot find function `f2` in crate `d2`\n --> d1/src/main.rs:6:17",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn feature_resolution_works_for_cfg_target_specification() {
+ if cross_compile::disabled() {
+ return;
+ }
+ let target = cross_compile::alternate();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ resolver = "2"
+
+ [dependencies.d1]
+ path = "d1"
+ artifact = "bin"
+ target = "$TARGET"
+ "#
+ .replace("$TARGET", target),
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_D1"));
+ }
+ "#,
+ )
+ .file(
+ "d1/Cargo.toml",
+ &r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ authors = []
+
+ [target.'$TARGET'.dependencies]
+ d2 = { path = "../d2" }
+ "#
+ .replace("$TARGET", target),
+ )
+ .file(
+ "d1/src/main.rs",
+ r#"fn main() {
+ d1::f();
+ }"#,
+ )
+ .file("d1/build.rs", r#"fn main() { }"#)
+ .file(
+ "d1/src/lib.rs",
+ &r#"pub fn f() {
+ #[cfg(target = "$TARGET")]
+ d2::f();
+ }
+ "#
+ .replace("$TARGET", target),
+ )
+ .file(
+ "d2/Cargo.toml",
+ r#"
+ [package]
+ name = "d2"
+ version = "0.0.1"
+ authors = []
+ "#,
+ )
+ .file("d2/build.rs", r#"fn main() { }"#)
+ .file("d2/src/lib.rs", "pub fn f() {}")
+ .build();
+
+ p.cargo("test -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .run();
+}
+
+#[cargo_test]
+fn build_script_with_bin_artifacts() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = ["bin", "staticlib", "cdylib"] }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ let baz: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_baz").expect("CARGO_BIN_FILE_BAR_baz").into();
+ println!("{}", baz.display());
+ assert!(&baz.is_file());
+
+ let lib: std::path::PathBuf = std::env::var("CARGO_STATICLIB_FILE_BAR_bar").expect("CARGO_STATICLIB_FILE_BAR_bar").into();
+ println!("{}", lib.display());
+ assert!(&lib.is_file());
+
+ let lib: std::path::PathBuf = std::env::var("CARGO_CDYLIB_FILE_BAR_bar").expect("CARGO_CDYLIB_FILE_BAR_bar").into();
+ println!("{}", lib.display());
+ assert!(&lib.is_file());
+
+ let dir: std::path::PathBuf = std::env::var("CARGO_BIN_DIR_BAR").expect("CARGO_BIN_DIR_BAR").into();
+ println!("{}", dir.display());
+ assert!(dir.is_dir());
+
+ let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into();
+ println!("{}", bar.display());
+ assert!(&bar.is_file());
+
+ let bar2: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_bar").expect("CARGO_BIN_FILE_BAR_bar").into();
+ println!("{}", bar2.display());
+ assert_eq!(bar, bar2);
+ }
+ "#)
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.5.0"
+ authors = []
+
+ [lib]
+ crate-type = ["staticlib", "cdylib"]
+ "#,
+ )
+ // compilation target is native for build scripts unless overridden
+ .file("bar/src/bin/bar.rs", &format!(r#"fn main() {{ assert_eq!(std::env::var("TARGET").unwrap(), "{}"); }}"#, cross_compile::native()))
+ .file("bar/src/bin/baz.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "")
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains("[COMPILING] foo [..]")
+ .with_stderr_contains("[COMPILING] bar v0.5.0 ([CWD]/bar)")
+ .with_stderr_contains("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
+ .run();
+
+ let build_script_output = build_script_output_string(&p, "foo");
+ let msg = "we need the binary directory for this artifact along with all binary paths";
+ if cfg!(target_env = "msvc") {
+ match_exact(
+ "[..]/artifact/bar-[..]/bin/baz.exe\n\
+ [..]/artifact/bar-[..]/staticlib/bar-[..].lib\n\
+ [..]/artifact/bar-[..]/cdylib/bar.dll\n\
+ [..]/artifact/bar-[..]/bin\n\
+ [..]/artifact/bar-[..]/bin/bar.exe\n\
+ [..]/artifact/bar-[..]/bin/bar.exe",
+ &build_script_output,
+ msg,
+ "",
+ None,
+ )
+ .unwrap();
+ } else {
+ match_exact(
+ "[..]/artifact/bar-[..]/bin/baz-[..]\n\
+ [..]/artifact/bar-[..]/staticlib/libbar-[..].a\n\
+ [..]/artifact/bar-[..]/cdylib/[..]bar.[..]\n\
+ [..]/artifact/bar-[..]/bin\n\
+ [..]/artifact/bar-[..]/bin/bar-[..]\n\
+ [..]/artifact/bar-[..]/bin/bar-[..]",
+ &build_script_output,
+ msg,
+ "",
+ None,
+ )
+ .unwrap();
+ }
+
+ assert!(
+ !p.bin("bar").is_file(),
+ "artifacts are located in their own directory, exclusively, and won't be lifted up"
+ );
+ assert!(!p.bin("baz").is_file(),);
+ assert_artifact_executable_output(&p, "debug", "bar", "bar");
+}
+
+#[cargo_test]
+fn build_script_with_bin_artifact_and_lib_false() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ bar::doit()
+ }
+ "#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() { bar::doit(); }")
+ .file(
+ "bar/src/lib.rs",
+ r#"
+ pub fn doit() {
+ panic!("sentinel");
+ }
+ "#,
+ )
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr_does_not_contain("[..]sentinel[..]")
+ .run();
+}
+
+#[cargo_test]
+fn lib_with_bin_artifact_and_lib_false() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ pub fn foo() {
+ bar::doit()
+ }"#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() { bar::doit(); }")
+ .file(
+ "bar/src/lib.rs",
+ r#"
+ pub fn doit() {
+ panic!("sentinel");
+ }
+ "#,
+ )
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr_does_not_contain("[..]sentinel[..]")
+ .run();
+}
+
+#[cargo_test]
+fn build_script_with_selected_dashed_bin_artifact_and_lib_true() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar-baz = { path = "bar/", artifact = "bin:baz-suffix", lib = true }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ bar_baz::print_env()
+ }
+ "#)
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar-baz"
+ version = "0.5.0"
+ authors = []
+
+ [[bin]]
+ name = "bar"
+
+ [[bin]]
+ name = "baz-suffix"
+ "#,
+ )
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", r#"
+ pub fn print_env() {
+ let dir: std::path::PathBuf = std::env::var("CARGO_BIN_DIR_BAR_BAZ").expect("CARGO_BIN_DIR_BAR_BAZ").into();
+ let bin: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_BAZ_baz-suffix").expect("CARGO_BIN_FILE_BAR_BAZ_baz-suffix").into();
+ println!("{}", dir.display());
+ println!("{}", bin.display());
+ assert!(dir.is_dir());
+ assert!(&bin.is_file());
+ assert!(std::env::var("CARGO_BIN_FILE_BAR_BAZ").is_err(), "CARGO_BIN_FILE_BAR_BAZ isn't set due to name mismatch");
+ assert!(std::env::var("CARGO_BIN_FILE_BAR_BAZ_bar").is_err(), "CARGO_BIN_FILE_BAR_BAZ_bar isn't set as binary isn't selected");
+ }
+ "#)
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar-baz v0.5.0 ([CWD]/bar)
+[COMPILING] foo [..]
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+ )
+ .run();
+
+ let build_script_output = build_script_output_string(&p, "foo");
+ let msg = "we need the binary directory for this artifact and the binary itself";
+
+ if cfg!(target_env = "msvc") {
+ cargo_test_support::compare::match_exact(
+ &format!(
+ "[..]/artifact/bar-baz-[..]/bin\n\
+ [..]/artifact/bar-baz-[..]/bin/baz_suffix{}",
+ std::env::consts::EXE_SUFFIX,
+ ),
+ &build_script_output,
+ msg,
+ "",
+ None,
+ )
+ .unwrap();
+ } else {
+ cargo_test_support::compare::match_exact(
+ "[..]/artifact/bar-baz-[..]/bin\n\
+ [..]/artifact/bar-baz-[..]/bin/baz_suffix-[..]",
+ &build_script_output,
+ msg,
+ "",
+ None,
+ )
+ .unwrap();
+ }
+
+ assert!(
+ !p.bin("bar").is_file(),
+ "artifacts are located in their own directory, exclusively, and won't be lifted up"
+ );
+ assert_artifact_executable_output(&p, "debug", "bar", "baz_suffix");
+}
+
+#[cargo_test]
+fn lib_with_selected_dashed_bin_artifact_and_lib_true() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar-baz = { path = "bar/", artifact = ["bin:baz-suffix", "staticlib", "cdylib"], lib = true }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ pub fn foo() {
+ bar_baz::exists();
+
+ env!("CARGO_BIN_DIR_BAR_BAZ");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_BAZ_baz-suffix"));
+ let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ"));
+ let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_BAZ_bar-baz"));
+ let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ"));
+ let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_BAZ_bar-baz"));
+ }
+ "#,
+ )
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar-baz"
+ version = "0.5.0"
+ authors = []
+
+ [lib]
+ crate-type = ["rlib", "staticlib", "cdylib"]
+
+ [[bin]]
+ name = "bar"
+
+ [[bin]]
+ name = "baz-suffix"
+ "#,
+ )
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn exists() {}")
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar-baz v0.5.0 ([CWD]/bar)
+[COMPILING] foo [..]
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+ )
+ .run();
+
+ assert!(
+ !p.bin("bar").is_file(),
+ "artifacts are located in their own directory, exclusively, and won't be lifted up"
+ );
+ assert_artifact_executable_output(&p, "debug", "bar", "baz_suffix");
+}
+
+#[cargo_test]
+fn allow_artifact_and_no_artifact_dep_to_same_package_within_different_dep_categories() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+
+ [dev-dependencies]
+ bar = { path = "bar/", package = "bar" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #[cfg(test)] extern crate bar;
+ pub fn foo() {
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ }"#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "")
+ .build();
+ p.cargo("test -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains("[COMPILING] bar v0.5.0 ([CWD]/bar)")
+ .with_stderr_contains("[FINISHED] test [unoptimized + debuginfo] target(s) in [..]")
+ .run();
+}
+
+#[cargo_test]
+fn normal_build_deps_are_picked_up_in_presence_of_an_artifact_build_dep_to_the_same_package() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar", artifact = "bin:bar" }
+
+ [build-dependencies]
+ bar = { path = "bar" }
+ "#,
+ )
+ .file("build.rs", "fn main() { bar::f(); }")
+ .file(
+ "src/lib.rs",
+ r#"
+ pub fn foo() {
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ }"#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn f() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .run();
+}
+
+#[cargo_test]
+fn disallow_using_example_binaries_as_artifacts() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin:one-example" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/examples/one-example.rs", "fn main() {}")
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr(r#"[ERROR] dependency `bar` in package `foo` requires a `bin:one-example` artifact to be present."#)
+ .run();
+}
+
+/// From RFC 3028
+///
+/// > You may also specify separate dependencies with different artifact values, as well as
+/// dependencies on the same crate without artifact specified; for instance, you may have a
+/// build dependency on the binary of a crate and a normal dependency on the Rust library of the same crate.
+#[cargo_test]
+fn allow_artifact_and_non_artifact_dependency_to_same_crate() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+
+ [dependencies]
+ bar = { path = "bar/" }
+ "#,
+ )
+ .file("src/lib.rs", r#"
+ pub fn foo() {
+ bar::doit();
+ assert!(option_env!("CARGO_BIN_FILE_BAR").is_none());
+ }"#)
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ assert!(option_env!("CARGO_BIN_FILE_BAR").is_none(), "no environment variables at build time");
+ std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap();
+ }"#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn doit() {}")
+ .build();
+
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains("[COMPILING] bar [..]")
+ .with_stderr_contains("[COMPILING] foo [..]")
+ .run();
+}
+
+#[cargo_test]
+fn build_script_deps_adopt_specified_target_unconditionally() {
+ if cross_compile::disabled() {
+ return;
+ }
+
+ let target = cross_compile::alternate();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies.bar]
+ path = "bar/"
+ artifact = "bin"
+ target = "{}"
+ "#,
+ target
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into();
+ assert!(&bar.is_file());
+ }"#)
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn doit() {}")
+ .build();
+
+ p.cargo("check -v -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_does_not_contain(format!(
+ "[RUNNING] `rustc --crate-name build_script_build build.rs [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build build.rs [..]")
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_does_not_contain(format!(
+ "[RUNNING] `rustc --crate-name foo [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]")
+ .run();
+}
+
+/// inverse RFC-3176
+#[cargo_test]
+fn build_script_deps_adopt_do_not_allow_multiple_targets_under_different_name_and_same_version() {
+ if cross_compile::disabled() {
+ return;
+ }
+
+ let alternate = cross_compile::alternate();
+ let native = cross_compile::native();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies.bar]
+ path = "bar/"
+ artifact = "bin"
+ target = "{}"
+
+ [build-dependencies.bar-native]
+ package = "bar"
+ path = "bar/"
+ artifact = "bin"
+ target = "{}"
+ "#,
+ alternate,
+ native
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into();
+ assert!(&bar.is_file());
+ let bar_native: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR_NATIVE_bar").expect("CARGO_BIN_FILE_BAR_NATIVE_bar").into();
+ assert!(&bar_native.is_file());
+ assert_ne!(bar_native, bar, "should build different binaries due to different targets");
+ }"#)
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check -v -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr(format!(
+ "error: the crate `foo v0.0.0 ([CWD])` depends on crate `bar v0.5.0 ([CWD]/bar)` multiple times with different names",
+ ))
+ .run();
+}
+
+#[cargo_test]
+fn non_build_script_deps_adopt_specified_target_unconditionally() {
+ if cross_compile::disabled() {
+ return;
+ }
+
+ let target = cross_compile::alternate();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies.bar]
+ path = "bar/"
+ artifact = "bin"
+ target = "{}"
+ "#,
+ target
+ ),
+ )
+ .file(
+ "src/lib.rs",
+ r#"pub fn foo() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn doit() {}")
+ .build();
+
+ p.cargo("check -v -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_does_not_contain(format!(
+ "[RUNNING] `rustc --crate-name foo [..]--target {} [..]",
+ target
+ ))
+ .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]")
+ .run();
+}
+
+#[cargo_test]
+fn no_cross_doctests_works_with_artifacts() {
+ if cross_compile::disabled() {
+ return;
+ }
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin", lib = true }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ //! ```
+ //! env!("CARGO_BIN_DIR_BAR");
+ //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ //! ```
+ pub fn foo() {
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ }
+ "#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/lib.rs", r#"pub extern "C" fn c() {}"#)
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+
+ let target = rustc_host();
+ p.cargo("test -Z bindeps --target")
+ .arg(&target)
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.5.0 ([CWD]/bar)
+[COMPILING] foo v0.0.1 ([CWD])
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] [..] (target/{triple}/debug/deps/foo-[..][EXE])
+[DOCTEST] foo
+",
+ triple = target
+ ))
+ .run();
+
+ println!("c");
+ let target = cross_compile::alternate();
+
+ // This will build the library, but does not build or run doc tests.
+ // This should probably be a warning or error.
+ p.cargo("test -Z bindeps -v --doc --target")
+ .arg(&target)
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains(format!(
+ "[COMPILING] bar v0.5.0 ([CWD]/bar)
+[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {triple} [..]
+[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {triple} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc --crate-name foo [..]
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]",
+ triple = target
+ ))
+ .run();
+
+ if !cross_compile::can_run_on_host() {
+ return;
+ }
+
+ // This tests the library, but does not run the doc tests.
+ p.cargo("test -Z bindeps -v --target")
+ .arg(&target)
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains(&format!(
+ "[FRESH] bar v0.5.0 ([CWD]/bar)
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc --crate-name foo [..]--test[..]
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `[CWD]/target/{triple}/debug/deps/foo-[..][EXE]`",
+ triple = target
+ ))
+ .run();
+}
+
+#[cargo_test]
+fn build_script_deps_adopts_target_platform_if_target_equals_target() {
+ if cross_compile::disabled() {
+ return;
+ }
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin", target = "target" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into();
+ assert!(&bar.is_file());
+ }"#)
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn doit() {}")
+ .build();
+
+ let alternate_target = cross_compile::alternate();
+ p.cargo("check -v -Z bindeps --target")
+ .arg(alternate_target)
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_does_not_contain(format!(
+ "[RUNNING] `rustc --crate-name build_script_build build.rs [..]--target {} [..]",
+ alternate_target
+ ))
+ .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build build.rs [..]")
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--target {} [..]",
+ alternate_target
+ ))
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..]--target {} [..]",
+ alternate_target
+ ))
+ .with_stderr_contains(format!(
+ "[RUNNING] `rustc --crate-name foo [..]--target {} [..]",
+ alternate_target
+ ))
+ .run();
+}
+
+#[cargo_test]
+// TODO(ST): rename bar (dependency) to something else and un-ignore this with RFC-3176
+#[cfg_attr(target_env = "msvc", ignore = "msvc not working")]
+fn profile_override_basic() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [build-dependencies]
+ bar = { path = "bar", artifact = "bin" }
+
+ [dependencies]
+ bar = { path = "bar", artifact = "bin" }
+
+ [profile.dev.build-override]
+ opt-level = 1
+
+ [profile.dev]
+ opt-level = 3
+ "#,
+ )
+ .file("build.rs", "fn main() {}")
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("bar/src/lib.rs", "pub fn bar() {}")
+ .build();
+
+ p.cargo("build -v -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains(
+ "[RUNNING] `rustc --crate-name build_script_build [..] -C opt-level=1 [..]`",
+ )
+ .with_stderr_contains(
+ "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..] -C opt-level=3 [..]`",
+ )
+ .with_stderr_contains(
+ "[RUNNING] `rustc --crate-name bar bar/src/main.rs [..] -C opt-level=1 [..]`",
+ )
+ .with_stderr_contains(
+ "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] -C opt-level=1 [..]`",
+ )
+ .with_stderr_contains(
+ "[RUNNING] `rustc --crate-name bar bar/src/lib.rs [..] -C opt-level=3 [..]`",
+ )
+ .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..] -C opt-level=3 [..]`")
+ .run();
+}
+
+#[cargo_test]
+fn dependencies_of_dependencies_work_in_artifacts() {
+ Package::new("baz", "1.0.0")
+ .file("src/lib.rs", "pub fn baz() {}")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap();
+ }
+ "#,
+ )
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.5.0"
+ authors = []
+
+ [dependencies]
+ baz = "1.0.0"
+ "#,
+ )
+ .file("bar/src/lib.rs", r#"pub fn bar() {baz::baz()}"#)
+ .file("bar/src/main.rs", r#"fn main() {bar::bar()}"#)
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .run();
+
+ // cargo tree sees artifacts as the dependency kind they are in and doesn't do anything special with it.
+ p.cargo("tree -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stdout(
+ "\
+foo v0.0.0 ([CWD])
+[build-dependencies]
+└── bar v0.5.0 ([CWD]/bar)
+ └── baz v1.0.0
+",
+ )
+ .run();
+}
+
+// TODO: Fix this potentially by reverting 887562bfeb8c540594d7d08e6e9a4ab7eb255865 which adds artifact information to the registry
+// followed by 0ff93733626f7cbecaf9dce9ab62b4ced0be088e which picks it up.
+// For reference, see comments by ehuss https://github.com/rust-lang/cargo/pull/9992#discussion_r801086315 and
+// joshtriplett https://github.com/rust-lang/cargo/pull/9992#issuecomment-1033394197 .
+#[cargo_test]
+#[ignore = "broken, need artifact info in index"]
+fn targets_are_picked_up_from_non_workspace_artifact_deps() {
+ if cross_compile::disabled() {
+ return;
+ }
+ let target = cross_compile::alternate();
+ Package::new("artifact", "1.0.0")
+ .file("src/main.rs", r#"fn main() {}"#)
+ .file("src/lib.rs", r#"pub fn lib() {}"#)
+ .publish();
+
+ let mut dep = registry::Dependency::new("artifact", "1.0.0");
+ Package::new("uses-artifact", "1.0.0")
+ .file(
+ "src/lib.rs",
+ r#"pub fn uses_artifact() { let _b = include_bytes!(env!("CARGO_BIN_FILE_ARTIFACT")); }"#,
+ )
+ .add_dep(dep.artifact("bin", Some(target.to_string())))
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies]
+ uses-artifact = { version = "1.0.0" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"pub fn foo() { uses_artifact::uses_artifact(); }"#,
+ )
+ .build();
+
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .run();
+}
+
+#[cargo_test]
+fn allow_dep_renames_with_multiple_versions() {
+ Package::new("bar", "1.0.0")
+ .file("src/main.rs", r#"fn main() {println!("1.0.0")}"#)
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ bar_stable = { package = "bar", version = "1.0.0", artifact = "bin" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR").expect("BAR present")).status().unwrap();
+ std::process::Command::new(std::env::var("CARGO_BIN_FILE_BAR_STABLE_bar").expect("BAR STABLE present")).status().unwrap();
+ }
+ "#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", r#"fn main() {println!("0.5.0")}"#)
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains("[COMPILING] bar [..]")
+ .with_stderr_contains("[COMPILING] foo [..]")
+ .run();
+ let build_script_output = build_script_output_string(&p, "foo");
+ match_exact(
+ "0.5.0\n1.0.0",
+ &build_script_output,
+ "build script output",
+ "",
+ None,
+ )
+ .unwrap();
+}
+
+#[cargo_test]
+fn allow_artifact_and_non_artifact_dependency_to_same_crate_if_these_are_not_the_same_dep_kind() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin", lib = false }
+
+ [dependencies]
+ bar = { path = "bar/" }
+ "#,
+ )
+ .file("src/lib.rs", r#"
+ pub fn foo() {
+ bar::doit();
+ assert!(option_env!("CARGO_BIN_FILE_BAR").is_none());
+ }"#)
+ .file(
+ "build.rs",
+ r#"fn main() {
+ println!("{}", std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR"));
+ println!("{}", std::env::var("CARGO_BIN_FILE_BAR_bar").expect("CARGO_BIN_FILE_BAR_bar"));
+ }"#,
+ )
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "pub fn doit() {}")
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar [..]
+[COMPILING] foo [..]
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn prevent_no_lib_warning_with_artifact_dependencies() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"pub fn foo() { let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); }"#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+ [COMPILING] bar v0.5.0 ([CWD]/bar)\n\
+ [CHECKING] foo v0.0.0 ([CWD])\n\
+ [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn show_no_lib_warning_with_artifact_dependencies_that_have_no_lib_but_lib_true() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin", lib = true }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("src/build.rs", "fn main() {}")
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains("[WARNING] foo v0.0.0 ([CWD]) ignoring invalid dependency `bar` which is missing a lib target")
+ .with_stderr_contains("[COMPILING] bar v0.5.0 ([CWD]/bar)")
+ .with_stderr_contains("[CHECKING] foo [..]")
+ .with_stderr_contains("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
+ .run();
+}
+
+#[cargo_test]
+fn resolver_2_build_dep_without_lib() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ edition = "2021"
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", r#"
+ fn main() {
+ let bar: std::path::PathBuf = std::env::var("CARGO_BIN_FILE_BAR").expect("CARGO_BIN_FILE_BAR").into();
+ assert!(&bar.is_file());
+ }"#)
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .run();
+}
+
+#[cargo_test]
+fn check_missing_crate_type_in_package_fails() {
+ for crate_type in &["cdylib", "staticlib", "bin"] {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+
+ [dependencies]
+ bar = {{ path = "bar/", artifact = "{}" }}
+ "#,
+ crate_type
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) //no bin, just rlib
+ .file("bar/src/lib.rs", "")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr(
+ "[ERROR] dependency `bar` in package `foo` requires a `[..]` artifact to be present.",
+ )
+ .run();
+ }
+}
+
+#[cargo_test]
+fn check_target_equals_target_in_non_build_dependency_errors() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin", target = "target" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_status(101)
+ .with_stderr_contains(
+ " `target = \"target\"` in normal- or dev-dependencies has no effect (bar)",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn env_vars_and_build_products_for_various_build_targets() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+ authors = []
+ resolver = "2"
+
+ [lib]
+ doctest = true
+
+ [build-dependencies]
+ bar = { path = "bar/", artifact = ["cdylib", "staticlib"] }
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin", lib = true }
+
+ [dev-dependencies]
+ bar = { path = "bar/", artifact = "bin:baz" }
+ "#,
+ )
+ .file("build.rs", r#"
+ fn main() {
+ let file: std::path::PathBuf = std::env::var("CARGO_CDYLIB_FILE_BAR").expect("CARGO_CDYLIB_FILE_BAR").into();
+ assert!(&file.is_file());
+
+ let file: std::path::PathBuf = std::env::var("CARGO_STATICLIB_FILE_BAR").expect("CARGO_STATICLIB_FILE_BAR").into();
+ assert!(&file.is_file());
+
+ assert!(std::env::var("CARGO_BIN_FILE_BAR").is_err());
+ assert!(std::env::var("CARGO_BIN_FILE_BAR_baz").is_err());
+ }
+ "#)
+ .file(
+ "src/lib.rs",
+ r#"
+ //! ```
+ //! bar::c();
+ //! env!("CARGO_BIN_DIR_BAR");
+ //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar"));
+ //! let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz"));
+ //! assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none());
+ //! assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none());
+ //! ```
+ pub fn foo() {
+ bar::c();
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar"));
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz"));
+ assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none());
+ assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none());
+ }
+
+ #[cfg(test)]
+ #[test]
+ fn env_unit() {
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar"));
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz"));
+ assert!(option_env!("CARGO_STATICLIB_FILE_BAR").is_none());
+ assert!(option_env!("CARGO_CDYLIB_FILE_BAR").is_none());
+ }
+ "#,
+ )
+ .file(
+ "tests/main.rs",
+ r#"
+ #[test]
+ fn env_integration() {
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_bar"));
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR_baz"));
+ }"#,
+ )
+ .file("build.rs", "fn main() {}")
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.5.0"
+ authors = []
+
+ [lib]
+ crate-type = ["staticlib", "cdylib", "rlib"]
+
+ [[bin]]
+ name = "bar"
+
+ [[bin]]
+ name = "baz"
+ "#,
+ )
+ .file("bar/src/lib.rs", r#"pub extern "C" fn c() {}"#)
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+ p.cargo("test -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar [..]
+[COMPILING] foo [..]
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] unittests [..]
+[RUNNING] tests/main.rs [..]
+[DOCTEST] foo
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn publish_artifact_dep() {
+ let registry = RegistryBuilder::new().http_api().http_index().build();
+
+ Package::new("bar", "1.0.0").publish();
+ Package::new("baz", "1.0.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ license = "MIT"
+ description = "foo"
+ documentation = "foo"
+ homepage = "foo"
+ repository = "foo"
+ resolver = "2"
+
+ [dependencies]
+ bar = { version = "1.0", artifact = "bin", lib = true }
+
+ [build-dependencies]
+ baz = { version = "1.0", artifact = ["bin:a", "cdylib", "staticlib"], target = "target" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("publish -Z bindeps --no-verify")
+ .replace_crates_io(registry.index_url())
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[PACKAGING] foo v0.1.0 [..]
+[PACKAGED] [..]
+[UPLOADING] foo v0.1.0 [..]
+[UPLOADED] foo v0.1.0 [..]
+note: Waiting [..]
+You may press ctrl-c [..]
+[PUBLISHED] foo v0.1.0 [..]
+",
+ )
+ .run();
+
+ publish::validate_upload_with_contents(
+ r#"
+ {
+ "authors": [],
+ "badges": {},
+ "categories": [],
+ "deps": [{
+ "default_features": true,
+ "features": [],
+ "kind": "normal",
+ "name": "bar",
+ "optional": false,
+ "target": null,
+ "version_req": "^1.0"
+ },
+ {
+ "default_features": true,
+ "features": [],
+ "kind": "build",
+ "name": "baz",
+ "optional": false,
+ "target": null,
+ "version_req": "^1.0"
+ }
+ ],
+ "description": "foo",
+ "documentation": "foo",
+ "features": {},
+ "homepage": "foo",
+ "keywords": [],
+ "license": "MIT",
+ "license_file": null,
+ "links": null,
+ "name": "foo",
+ "readme": null,
+ "readme_file": null,
+ "repository": "foo",
+ "vers": "0.1.0"
+ }
+ "#,
+ "foo-0.1.0.crate",
+ &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
+ &[(
+ "Cargo.toml",
+ &format!(
+ r#"{}
+[package]
+name = "foo"
+version = "0.1.0"
+authors = []
+description = "foo"
+homepage = "foo"
+documentation = "foo"
+license = "MIT"
+repository = "foo"
+resolver = "2"
+
+[dependencies.bar]
+version = "1.0"
+artifact = ["bin"]
+lib = true
+
+[build-dependencies.baz]
+version = "1.0"
+artifact = [
+ "bin:a",
+ "cdylib",
+ "staticlib",
+]
+target = "target""#,
+ cargo::core::package::MANIFEST_PREAMBLE
+ ),
+ )],
+ );
+}
+
+#[cargo_test]
+fn doc_lib_true() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ resolver = "2"
+
+ [dependencies.bar]
+ path = "bar"
+ artifact = "bin"
+ lib = true
+ "#,
+ )
+ .file("src/lib.rs", "extern crate bar; pub fn foo() {}")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("bar/src/lib.rs", "pub fn bar() {}")
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("doc -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar v0.0.1 ([CWD]/bar)
+[DOCUMENTING] bar v0.0.1 ([CWD]/bar)
+[DOCUMENTING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+
+ assert!(p.root().join("target/doc").is_dir());
+ assert!(p.root().join("target/doc/foo/index.html").is_file());
+ assert!(p.root().join("target/doc/bar/index.html").is_file());
+
+ // Verify that it emits rmeta for the bin and lib dependency.
+ assert_eq!(p.glob("target/debug/artifact/*.rlib").count(), 0);
+ assert_eq!(p.glob("target/debug/deps/libbar-*.rmeta").count(), 2);
+
+ p.cargo("doc -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint")
+ .with_stdout("")
+ .run();
+
+ assert!(p.root().join("target/doc").is_dir());
+ assert!(p.root().join("target/doc/foo/index.html").is_file());
+ assert!(p.root().join("target/doc/bar/index.html").is_file());
+}
+
+#[cargo_test]
+fn rustdoc_works_on_libs_with_artifacts_and_lib_false() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ resolver = "2"
+
+ [dependencies.bar]
+ path = "bar"
+ artifact = ["bin", "staticlib", "cdylib"]
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ pub fn foo() {
+ env!("CARGO_BIN_DIR_BAR");
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR"));
+ let _b = include_bytes!(env!("CARGO_CDYLIB_FILE_BAR_bar"));
+ let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR"));
+ let _b = include_bytes!(env!("CARGO_STATICLIB_FILE_BAR_bar"));
+ }"#,
+ )
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.5.0"
+ authors = []
+
+ [lib]
+ crate-type = ["staticlib", "cdylib"]
+ "#,
+ )
+ .file("bar/src/lib.rs", "pub fn bar() {}")
+ .file("bar/src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("doc -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar v0.5.0 ([CWD]/bar)
+[DOCUMENTING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+
+ assert!(p.root().join("target/doc").is_dir());
+ assert!(p.root().join("target/doc/foo/index.html").is_file());
+ assert!(
+ !p.root().join("target/doc/bar/index.html").is_file(),
+ "bar is not a lib dependency and thus remains undocumented"
+ );
+}
+
+fn assert_artifact_executable_output(
+ p: &Project,
+ target_name: &str,
+ dep_name: &str,
+ bin_name: &str,
+) {
+ if cfg!(target_env = "msvc") {
+ assert_eq!(
+ p.glob(format!(
+ "target/{}/deps/artifact/{}-*/bin/{}{}",
+ target_name,
+ dep_name,
+ bin_name,
+ std::env::consts::EXE_SUFFIX
+ ))
+ .count(),
+ 1,
+ "artifacts are placed into their own output directory to not possibly clash"
+ );
+ } else {
+ assert_eq!(
+ p.glob(format!(
+ "target/{}/deps/artifact/{}-*/bin/{}-*{}",
+ target_name,
+ dep_name,
+ bin_name,
+ std::env::consts::EXE_SUFFIX
+ ))
+ .filter_map(Result::ok)
+ .filter(|f| f.extension().map_or(true, |ext| ext != "o" && ext != "d"))
+ .count(),
+ 1,
+ "artifacts are placed into their own output directory to not possibly clash"
+ );
+ }
+}
+
+fn build_script_output_string(p: &Project, package_name: &str) -> String {
+ let paths = p
+ .glob(format!("target/debug/build/{}-*/output", package_name))
+ .collect::<Result<Vec<_>, _>>()
+ .unwrap();
+ assert_eq!(paths.len(), 1);
+ std::fs::read_to_string(&paths[0]).unwrap()
+}
+
+#[cargo_test]
+fn build_script_features_for_shared_dependency() {
+ // When a build script is built and run, its features should match. Here:
+ //
+ // foo
+ // -> artifact on d1 with target
+ // -> common with features f1
+ //
+ // d1
+ // -> common with features f2
+ //
+ // common has features f1 and f2, with a build script.
+ //
+ // When common is built as a dependency of d1, it should have features
+ // `f2` (for the library and the build script).
+ //
+ // When common is built as a dependency of foo, it should have features
+ // `f1` (for the library and the build script).
+ if cross_compile::disabled() {
+ return;
+ }
+ let target = cross_compile::alternate();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ resolver = "2"
+
+ [dependencies]
+ d1 = { path = "d1", artifact = "bin", target = "$TARGET" }
+ common = { path = "common", features = ["f1"] }
+ "#
+ .replace("$TARGET", target),
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_D1"));
+ common::f1();
+ }
+ "#,
+ )
+ .file(
+ "d1/Cargo.toml",
+ r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+
+ [dependencies]
+ common = { path = "../common", features = ["f2"] }
+ "#,
+ )
+ .file(
+ "d1/src/main.rs",
+ r#"fn main() {
+ common::f2();
+ }"#,
+ )
+ .file(
+ "common/Cargo.toml",
+ r#"
+ [package]
+ name = "common"
+ version = "0.0.1"
+
+ [features]
+ f1 = []
+ f2 = []
+ "#,
+ )
+ .file(
+ "common/src/lib.rs",
+ r#"
+ #[cfg(feature = "f1")]
+ pub fn f1() {}
+
+ #[cfg(feature = "f2")]
+ pub fn f2() {}
+ "#,
+ )
+ .file(
+ "common/build.rs",
+ &r#"
+ use std::env::var_os;
+ fn main() {
+ assert_eq!(var_os("CARGO_FEATURE_F1").is_some(), cfg!(feature="f1"));
+ assert_eq!(var_os("CARGO_FEATURE_F2").is_some(), cfg!(feature="f2"));
+ if std::env::var("TARGET").unwrap() == "$TARGET" {
+ assert!(var_os("CARGO_FEATURE_F1").is_none());
+ assert!(var_os("CARGO_FEATURE_F2").is_some());
+ } else {
+ assert!(var_os("CARGO_FEATURE_F1").is_some());
+ assert!(var_os("CARGO_FEATURE_F2").is_none());
+ }
+ }
+ "#
+ .replace("$TARGET", target),
+ )
+ .build();
+
+ p.cargo("build -Z bindeps -v")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .run();
+}
+
+#[cargo_test]
+fn calc_bin_artifact_fingerprint() {
+ // See rust-lang/cargo#10527
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ resolver = "2"
+
+ [dependencies]
+ bar = { path = "bar/", artifact = "bin" }
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR"));
+ }
+ "#,
+ )
+ .file("bar/Cargo.toml", &basic_bin_manifest("bar"))
+ .file("bar/src/main.rs", r#"fn main() { println!("foo") }"#)
+ .build();
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] bar v0.5.0 ([CWD]/bar)
+[CHECKING] foo v0.1.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+
+ p.change_file("bar/src/main.rs", r#"fn main() { println!("bar") }"#);
+ // Change in artifact bin dep `bar` propagates to `foo`, triggering recompile.
+ p.cargo("check -v -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[DIRTY] bar v0.5.0 ([CWD]/bar): the file `bar/src/main.rs` has changed ([..])
+[COMPILING] bar v0.5.0 ([CWD]/bar)
+[RUNNING] `rustc --crate-name bar [..]`
+[DIRTY] foo v0.1.0 ([CWD]): the dependency bar was rebuilt
+[CHECKING] foo v0.1.0 ([CWD])
+[RUNNING] `rustc --crate-name foo [..]`
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+
+ // All units are fresh. No recompile.
+ p.cargo("check -v -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[FRESH] bar v0.5.0 ([CWD]/bar)
+[FRESH] foo v0.1.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn with_target_and_optional() {
+ // See rust-lang/cargo#10526
+ if cross_compile::disabled() {
+ return;
+ }
+ let target = cross_compile::alternate();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ edition = "2021"
+ [dependencies]
+ d1 = { path = "d1", artifact = "bin", optional = true, target = "$TARGET" }
+ "#
+ .replace("$TARGET", target),
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ let _b = include_bytes!(env!("CARGO_BIN_FILE_D1"));
+ }
+ "#,
+ )
+ .file(
+ "d1/Cargo.toml",
+ r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ edition = "2021"
+ "#,
+ )
+ .file("d1/src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check -Z bindeps -F d1 -v")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] d1 v0.0.1 [..]
+[RUNNING] `rustc --crate-name d1 [..]--crate-type bin[..]
+[CHECKING] foo v0.0.1 [..]
+[RUNNING] `rustc --crate-name foo [..]--cfg[..]d1[..]
+[FINISHED] dev [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn with_assumed_host_target_and_optional_build_dep() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ edition = "2021"
+ [build-dependencies]
+ d1 = { path = "d1", artifact = "bin", optional = true, target = "target" }
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ std::env::var("CARGO_BIN_FILE_D1").unwrap();
+ }
+ "#,
+ )
+ .file(
+ "d1/Cargo.toml",
+ r#"
+ [package]
+ name = "d1"
+ version = "0.0.1"
+ edition = "2021"
+ "#,
+ )
+ .file("d1/src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check -Z bindeps -F d1 -v")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_unordered(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[COMPILING] d1 v0.0.1 ([CWD]/d1)
+[RUNNING] `rustc --crate-name build_script_build [..]--crate-type bin[..]
+[RUNNING] `rustc --crate-name d1 [..]--crate-type bin[..]
+[RUNNING] `[CWD]/target/debug/build/foo-[..]/build-script-build`
+[RUNNING] `rustc --crate-name foo [..]--cfg[..]d1[..]
+[FINISHED] dev [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn decouple_same_target_transitive_dep_from_artifact_dep() {
+ // See https://github.com/rust-lang/cargo/issues/11463
+ let target = rustc_host();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ a = {{ path = "a" }}
+ bar = {{ path = "bar", artifact = "bin", target = "{target}" }}
+ "#
+ ),
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {}
+ "#,
+ )
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.1.0"
+
+ [dependencies]
+ a = { path = "../a", features = ["feature"] }
+ "#,
+ )
+ .file(
+ "bar/src/main.rs",
+ r#"
+ fn main() {}
+ "#,
+ )
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ b = { path = "../b" }
+ c = { path = "../c" }
+
+ [features]
+ feature = ["c/feature"]
+ "#,
+ )
+ .file(
+ "a/src/lib.rs",
+ r#"
+ use b::Trait as _;
+
+ pub fn use_b_trait(x: &impl c::Trait) {
+ x.b();
+ }
+ "#,
+ )
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "0.1.0"
+
+ [dependencies]
+ c = { path = "../c" }
+ "#,
+ )
+ .file(
+ "b/src/lib.rs",
+ r#"
+ pub trait Trait {
+ fn b(&self) {}
+ }
+
+ impl<T: c::Trait> Trait for T {}
+ "#,
+ )
+ .file(
+ "c/Cargo.toml",
+ r#"
+ [package]
+ name = "c"
+ version = "0.1.0"
+
+ [features]
+ feature = []
+ "#,
+ )
+ .file(
+ "c/src/lib.rs",
+ r#"
+ pub trait Trait {}
+ "#,
+ )
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] c v0.1.0 ([CWD]/c)
+[COMPILING] b v0.1.0 ([CWD]/b)
+[COMPILING] a v0.1.0 ([CWD]/a)
+[COMPILING] bar v0.1.0 ([CWD]/bar)
+[COMPILING] foo v0.1.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn decouple_same_target_transitive_dep_from_artifact_dep_lib() {
+ // See https://github.com/rust-lang/cargo/issues/10837
+ let target = rustc_host();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ a = {{ path = "a" }}
+ b = {{ path = "b", features = ["feature"] }}
+ bar = {{ path = "bar", artifact = "bin", lib = true, target = "{target}" }}
+ "#
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ a = { path = "../a", features = ["b"] }
+ b = { path = "../b" }
+ "#,
+ )
+ .file("bar/src/lib.rs", "")
+ .file(
+ "bar/src/main.rs",
+ r#"
+ use b::Trait;
+
+ fn main() {
+ a::A.b()
+ }
+ "#,
+ )
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+
+ [dependencies]
+ b = { path = "../b", optional = true }
+ "#,
+ )
+ .file(
+ "a/src/lib.rs",
+ r#"
+ pub struct A;
+
+ #[cfg(feature = "b")]
+ impl b::Trait for A {}
+ "#,
+ )
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "0.1.0"
+
+ [features]
+ feature = []
+ "#,
+ )
+ .file(
+ "b/src/lib.rs",
+ r#"
+ pub trait Trait {
+ fn b(&self) {}
+ }
+ "#,
+ )
+ .build();
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] b v0.1.0 ([CWD]/b)
+[COMPILING] a v0.1.0 ([CWD]/a)
+[COMPILING] bar v0.1.0 ([CWD]/bar)
+[COMPILING] foo v0.1.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn decouple_same_target_transitive_dep_from_artifact_dep_and_proc_macro() {
+ let target = rustc_host();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ c = {{ path = "c" }}
+ bar = {{ path = "bar", artifact = "bin", target = "{target}" }}
+ "#
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.1.0"
+
+ [dependencies]
+ b = { path = "../b" }
+ "#,
+ )
+ .file("bar/src/main.rs", "fn main() {}")
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ a = { path = "../a" }
+
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file("b/src/lib.rs", "")
+ .file(
+ "c/Cargo.toml",
+ r#"
+ [package]
+ name = "c"
+ version = "0.1.0"
+ edition = "2021"
+
+ [dependencies]
+ d = { path = "../d", features = ["feature"] }
+ a = { path = "../a" }
+
+ [lib]
+ proc-macro = true
+ "#,
+ )
+ .file(
+ "c/src/lib.rs",
+ r#"
+ use a::Trait;
+
+ fn _c() {
+ d::D.a()
+ }
+ "#,
+ )
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+
+ [dependencies]
+ d = { path = "../d" }
+ "#,
+ )
+ .file(
+ "a/src/lib.rs",
+ r#"
+ pub trait Trait {
+ fn a(&self) {}
+ }
+
+ impl Trait for d::D {}
+ "#,
+ )
+ .file(
+ "d/Cargo.toml",
+ r#"
+ [package]
+ name = "d"
+ version = "0.1.0"
+
+ [features]
+ feature = []
+ "#,
+ )
+ .file("d/src/lib.rs", "pub struct D;")
+ .build();
+
+ p.cargo("build -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_unordered(
+ "\
+[COMPILING] d v0.1.0 ([CWD]/d)
+[COMPILING] a v0.1.0 ([CWD]/a)
+[COMPILING] b v0.1.0 ([CWD]/b)
+[COMPILING] c v0.1.0 ([CWD]/c)
+[COMPILING] bar v0.1.0 ([CWD]/bar)
+[COMPILING] foo v0.1.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn same_target_artifact_dep_sharing() {
+ let target = rustc_host();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ a = {{ path = "a" }}
+ bar = {{ path = "bar", artifact = "bin", target = "{target}" }}
+ "#
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.1.0"
+
+ [dependencies]
+ a = { path = "../a" }
+ "#,
+ )
+ .file(
+ "bar/src/main.rs",
+ r#"
+ fn main() {}
+ "#,
+ )
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.1.0"
+ "#,
+ )
+ .file("a/src/lib.rs", "")
+ .build();
+ p.cargo(&format!("build -Z bindeps --target {target}"))
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr(
+ "\
+[COMPILING] a v0.1.0 ([CWD]/a)
+[COMPILING] bar v0.1.0 ([CWD]/bar)
+[COMPILING] foo v0.1.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn check_transitive_artifact_dependency_with_different_target() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [dependencies]
+ bar = { path = "bar/" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.0.0"
+
+ [dependencies]
+ baz = { path = "baz/", artifact = "bin", target = "custom-target" }
+ "#,
+ )
+ .file("bar/src/lib.rs", "")
+ .file(
+ "bar/baz/Cargo.toml",
+ r#"
+ [package]
+ name = "baz"
+ version = "0.0.0"
+
+ [dependencies]
+ "#,
+ )
+ .file("bar/baz/src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check -Z bindeps")
+ .masquerade_as_nightly_cargo(&["bindeps"])
+ .with_stderr_contains(
+ "error: could not find specification for target `custom-target`.\n \
+ Dependency `baz v0.0.0 [..]` requires to build for target `custom-target`.",
+ )
+ .with_status(101)
+ .run();
+}