//! Tests specific to artifact dependencies, designated using //! the new `dep = { artifact = "bin", … }` syntax in manifests. use cargo_test_support::compare::assert_e2e; use cargo_test_support::prelude::*; use cargo_test_support::registry::{Package, RegistryBuilder}; use cargo_test_support::str; 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" edition = "2015" 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_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/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" edition = "2015" 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_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/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" edition = "2015" 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_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/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" edition = "2015" 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_data(str![[r#" [ERROR] failed to run `rustc` to learn about target-specific information Caused by: process didn't exit successfully: `rustc - --crate-name ___ --print=file-names --target unknown-target-triple [..]` ([EXIT_STATUS]: 1) --- stderr [ERROR] Error loading target specification: Could not find specification for target "unknown-target-triple". Run `rustc --print target-list` for a list of built-in targets "#]]) .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" edition = "2015" 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_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [WARNING] foo v0.0.0 ([ROOT]/foo) ignoring invalid dependency `bar_stable` which is missing a lib target [ERROR] the crate `foo v0.0.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/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" edition = "2015" 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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] d2 v0.0.1 ([ROOT]/foo/d2) [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" 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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] d2 v0.0.1 ([ROOT]/foo/d2) [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) error[E0425]: cannot find function `f2` in crate `d2` ... For more information about this error, try `rustc --explain E0425`. [ERROR] could not compile `d1` (bin "d1") due to 1 previous error ... "#]]) .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" edition = "2015" 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" edition = "2015" 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" edition = "2015" 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" edition = "2015" 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" edition = "2015" 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_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] foo v0.0.0 ([ROOT]/foo) [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); let build_script_output = build_script_output_string(&p, "foo"); // we need the binary directory for this artifact along with all binary paths if cfg!(target_env = "msvc") { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/baz[EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/staticlib/bar-[HASH].lib [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/cdylib/bar.dll [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar[EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar[EXE] "#]], ); } else { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/baz-[HASH][EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/staticlib/libbar-[HASH].a [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/cdylib/[..]bar.[..] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar-[HASH][EXE] [ROOT]/foo/target/debug/deps/artifact/bar-[HASH]/bin/bar-[HASH][EXE] "#]], ); } 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" edition = "2015" 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(); #[allow(deprecated)] 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" edition = "2015" 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(); #[allow(deprecated)] 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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar-baz v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); let build_script_output = build_script_output_string(&p, "foo"); // we need the binary directory for this artifact and the binary itself if cfg!(target_env = "msvc") { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin/baz_suffix[EXE] "#]], ); } else { assert_e2e().eq( &build_script_output, str![[r#" [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin [ROOT]/foo/target/debug/deps/artifact/bar-baz-[HASH]/bin/baz_suffix-[HASH][EXE] "#]], ); } 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" edition = "2015" 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_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")); 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar-baz v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" 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(); #[allow(deprecated)] p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_does_not_contain(format!( "[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]--target {} [..]", target )) .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]") .with_stderr_contains(format!( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target {} [..]", target )) .with_stderr_contains(format!( "[RUNNING] `rustc --crate-name bar --edition=2015 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [ERROR] the crate `foo v0.0.0 ([ROOT]/foo)` depends on crate `bar v0.5.0 ([ROOT]/foo/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" edition = "2015" 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(); #[allow(deprecated)] p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_contains(format!( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target {} [..]", target )) .with_stderr_contains(format!( "[RUNNING] `rustc --crate-name bar --edition=2015 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/[HOST_TARGET]/debug/deps/foo-[HASH][EXE]) [DOCTEST] foo "#]]) .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_data(str![[r#" [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target [ALT_TARGET] [..] [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..]--target [ALT_TARGET] [..] [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [NOTE] skipping doctests for foo v0.0.1 ([ROOT]/foo) (lib), cross-compilation doctests are not yet supported See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#doctest-xcompile for more information. "#]]) .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_data(str![[r#" [FRESH] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]--test[..] [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/foo/target/[ALT_TARGET]/debug/deps/foo-[HASH][EXE]` [NOTE] skipping doctests for foo v0.0.1 ([ROOT]/foo) (lib), cross-compilation doctests are not yet supported See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#doctest-xcompile for more information. "#]]) .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" edition = "2015" 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(); #[allow(deprecated)] 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 --edition=2015 build.rs [..]--target {} [..]", alternate_target )) .with_stderr_contains("[RUNNING] `rustc --crate-name build_script_build --edition=2015 build.rs [..]") .with_stderr_contains(format!( "[RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--target {} [..]", alternate_target )) .with_stderr_contains(format!( "[RUNNING] `rustc --crate-name bar --edition=2015 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" edition = "2015" 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_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name build_script_build [..] -C opt-level=1 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..] -C opt-level=3 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/main.rs [..] -C opt-level=1 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..] -C opt-level=1 [..]` [RUNNING] `rustc --crate-name bar --edition=2015 bar/src/lib.rs [..] -C opt-level=3 [..]` [RUNNING] `rustc --crate-name foo [..] -C opt-level=3 [..]` [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" foo v0.0.0 ([ROOT]/foo) [build-dependencies] └── bar v0.5.0 ([ROOT]/foo/bar) └── baz v1.0.0 "#]]) .run(); } #[cargo_test] fn artifact_dep_target_specified() { if cross_compile::disabled() { return; } let target = cross_compile::alternate(); let p = project() .file( "Cargo.toml", &r#" [package] name = "foo" version = "0.0.0" authors = [] resolver = "2" edition = "2015" [dependencies] bindep = { path = "bindep", artifact = "bin", target = "$TARGET" } "# .replace("$TARGET", target), ) .file("src/lib.rs", "") .file("bindep/Cargo.toml", &basic_manifest("bindep", "0.0.0")) .file("bindep/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bindep v0.0.0 ([ROOT]/foo/bindep) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .with_status(0) .run(); // TODO: This command currently fails due to a bug in cargo but it should be fixed so that it succeeds in the future. p.cargo("tree -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stdout_data(str![[""]]) .with_stderr_data(str![[r#" thread 'main' panicked at src/cargo/[..]: activated_features for invalid package: features did not find PackageId { name: "bindep", version: "0.0.0", source: "[..]" } NormalOrDev [NOTE] run with `RUST_BACKTRACE=1` environment variable to display a backtrace "#]]) .with_status(101) .run(); } #[cargo_test] 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") .schema_version(3) .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" edition = "2015" 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 index_version_filtering() { 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("bar", "1.0.0").publish(); Package::new("bar", "1.0.1") .schema_version(3) .add_dep(dep.artifact("bin", Some(target.to_string()))) .publish(); // Verify that without `-Zbindeps` that it does not use 1.0.1. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]) .run(); // And with -Zbindeps it can use 1.0.1. p.cargo("update -Zbindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest compatible versions [ADDING] artifact v1.0.0 [UPDATING] bar v1.0.0 -> v1.0.1 "#]]) .run(); // And without -Zbindeps, now that 1.0.1 is in Cargo.lock, it should fail. p.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "^1.0"` (locked to 1.0.1) candidate versions found which didn't match: 1.0.0 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); } // FIXME: `download_accessible` should work properly for artifact dependencies #[cargo_test] #[ignore = "broken, needs download_accessible fix"] fn proc_macro_in_artifact_dep() { // Forcing FeatureResolver to check a proc-macro for a dependency behind a // target dependency. if cross_compile::disabled() { return; } Package::new("pm", "1.0.0") .file("src/lib.rs", "") .file( "Cargo.toml", r#" [package] name = "pm" version = "1.0.0" edition = "2015" [lib] proc-macro = true "#, ) .publish(); let alternate = cross_compile::alternate(); Package::new("bin-uses-pm", "1.0.0") .target_dep("pm", "1.0", alternate) .file("src/main.rs", "fn main() {}") .publish(); // Simulate a network error downloading the proc-macro. std::fs::remove_file(cargo_test_support::paths::root().join("dl/pm/1.0.0/download")).unwrap(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] bin-uses-pm = {{ version = "1.0", artifact = "bin", target = "{alternate}"}} "# ), ) .file("src/lib.rs", "") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#""#]]) .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" edition = "2015" 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_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [COMPILING] bar v1.0.0 [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); let build_script_output = build_script_output_string(&p, "foo"); assert_e2e().eq( &build_script_output, str![[r#" 0.5.0 1.0.0 "#]], ); } #[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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [WARNING] foo v0.0.0 ([ROOT]/foo) ignoring invalid dependency `bar` which is missing a lib target [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [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" edition = "2015" 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_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) [RUNNING] tests/main.rs (target/debug/deps/main-[HASH][EXE]) [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" edition = "2015" 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_data(str![[r#" [UPDATING] crates.io index [PACKAGING] foo v0.1.0 ([ROOT]/foo) [PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [UPLOADING] foo v0.1.0 ([ROOT]/foo) [UPLOADED] foo v0.1.0 to registry `crates-io` [NOTE] waiting for `foo v0.1.0` to be available at registry `crates-io`. You may press ctrl-c to skip waiting; the crate should be available shortly. [PUBLISHED] foo v0.1.0 at registry `crates-io` "#]]) .run(); publish::validate_upload_with_contents( r#" { "authors": [], "badges": {}, "categories": [], "deps": [{ "artifact": ["bin"], "default_features": true, "features": [], "kind": "normal", "lib": true, "name": "bar", "optional": false, "target": null, "version_req": "^1.0" }, { "artifact": [ "bin:a", "cdylib", "staticlib" ], "bindep_target": "target", "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", "rust_version": null, "vers": "0.1.0" } "#, "foo-0.1.0.crate", &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"], &[( "Cargo.toml", &format!( r#"{} [package] edition = "2015" name = "foo" version = "0.1.0" authors = [] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "foo" homepage = "foo" documentation = "foo" readme = false license = "MIT" repository = "foo" resolver = "2" [lib] name = "foo" path = "src/lib.rs" [dependencies.bar] version = "1.0" artifact = ["bin"] lib = true [build-dependencies.baz] version = "1.0" artifact = [ "bin:a", "cdylib", "staticlib", ] target = "target""#, cargo::core::manifest::MANIFEST_PREAMBLE ), )], ); } #[cargo_test] fn doc_lib_true() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .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"]) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .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" edition = "2015" 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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]]) .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::, _>>() .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" edition = "2015" 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" edition = "2015" [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" edition = "2015" [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" edition = "2015" 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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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_data(str![[r#" [DIRTY] bar v0.5.0 ([ROOT]/foo/bar): the file `bar/src/main.rs` has changed ([..]) [COMPILING] bar v0.5.0 ([ROOT]/foo/bar) [RUNNING] `rustc --crate-name bar [..]` [DIRTY] foo v0.1.0 ([ROOT]/foo): the dependency bar was rebuilt [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); // All units are fresh. No recompile. p.cargo("check -v -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [FRESH] bar v0.5.0 ([ROOT]/foo/bar) [FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [RUNNING] `rustc --crate-name d1 [..]--crate-type bin[..] [CHECKING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..]--cfg[..]d1[..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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_data( str![[r#" [LOCKING] 2 packages to latest compatible versions [COMPILING] foo v0.0.1 ([ROOT]/foo) [COMPILING] d1 v0.0.1 ([ROOT]/foo/d1) [RUNNING] `rustc --crate-name build_script_build --edition=2021 [..]--crate-type bin[..] [RUNNING] `rustc --crate-name d1 --edition=2021 [..]--crate-type bin[..] [RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [RUNNING] `rustc --crate-name foo --edition=2021 [..]--cfg[..]d1[..] [FINISHED] `dev` profile [..] "#]] .unordered(), ) .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" edition = "2015" [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" edition = "2015" [dependencies] c = { path = "../c" } "#, ) .file( "b/src/lib.rs", r#" pub trait Trait { fn b(&self) {} } impl Trait for T {} "#, ) .file( "c/Cargo.toml", r#" [package] name = "c" version = "0.1.0" edition = "2015" [features] feature = [] "#, ) .file( "c/src/lib.rs", r#" pub trait Trait {} "#, ) .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 5 packages to latest compatible versions [COMPILING] c v0.1.0 ([ROOT]/foo/c) [COMPILING] b v0.1.0 ([ROOT]/foo/b) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" [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" edition = "2015" [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_data(str![[r#" [LOCKING] 4 packages to latest compatible versions [COMPILING] b v0.1.0 ([ROOT]/foo/b) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" [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" edition = "2015" [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" edition = "2015" [features] feature = [] "#, ) .file("d/src/lib.rs", "pub struct D;") .build(); p.cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data( str![[r#" [LOCKING] 6 packages to latest compatible versions [COMPILING] d v0.1.0 ([ROOT]/foo/d) [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] b v0.1.0 ([ROOT]/foo/b) [COMPILING] c v0.1.0 ([ROOT]/foo/c) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..] "#]] .unordered(), ) .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" edition = "2015" [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" edition = "2015" [dependencies] a = { path = "../a" } "#, ) .file( "bar/src/main.rs", r#" fn main() {} "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" edition = "2015" "#, ) .file("a/src/lib.rs", "") .build(); p.cargo(&format!("build -Z bindeps --target {target}")) .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [COMPILING] a v0.1.0 ([ROOT]/foo/a) [COMPILING] bar v0.1.0 ([ROOT]/foo/bar) [COMPILING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .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" edition = "2015" [dependencies] bar = { path = "bar/" } "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.0.0" edition = "2015" [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" edition = "2015" [dependencies] "#, ) .file("bar/baz/src/main.rs", "fn main() {}") .build(); p.cargo("check -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .with_stderr_data(str![[r#" [LOCKING] 3 packages to latest compatible versions [ERROR] failed to determine target information for target `custom-target`. Artifact dependency `baz` in package `bar v0.0.0 ([ROOT]/foo/bar)` requires building for `custom-target` Caused by: failed to run `rustc` to learn about target-specific information Caused by: process didn't exit successfully: `rustc [..] ([EXIT_STATUS]: 1) --- stderr [ERROR] Error loading target specification: Could not find specification for target "custom-target". Run `rustc --print target-list` for a list of built-in targets "#]]) .with_status(101) .run(); } #[cargo_test] fn build_only_specified_artifact_library() { // Create a project with: // - A crate `bar` with both `staticlib` and `cdylib` as crate-types. // - A crate `foo` which depends on either the `staticlib` or `cdylib` artifact of bar, // whose build-script simply checks which library artifacts are present. let create_project = |artifact_lib| { project() .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [lib] crate-type = ["staticlib", "cdylib"] "#, ) .file("bar/src/lib.rs", "") .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "1.0.0" [build-dependencies] bar = {{ path = "bar", artifact = "{artifact_lib}" }} "#), ) .file("src/lib.rs", "") .file( "build.rs", r#" fn main() { println!("cdylib present: {}", std::env::var_os("CARGO_CDYLIB_FILE_BAR").is_some()); println!("staticlib present: {}", std::env::var_os("CARGO_STATICLIB_FILE_BAR").is_some()); } "#, ) .build() }; let cdylib = create_project("cdylib"); cdylib .cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); assert_e2e().eq( &build_script_output_string(&cdylib, "foo"), str![[r#" cdylib present: true staticlib present: false "#]], ); let staticlib = create_project("staticlib"); staticlib .cargo("build -Z bindeps") .masquerade_as_nightly_cargo(&["bindeps"]) .run(); assert_e2e().eq( &build_script_output_string(&staticlib, "foo"), str![[r#" cdylib present: false staticlib present: true "#]], ); }