//! Tests for when multiple artifacts have the same output filename. //! See https://github.com/rust-lang/cargo/issues/6313 for more details. //! Ideally these should never happen, but I don't think we'll ever be able to //! prevent all collisions. use cargo_test_support::prelude::*; use cargo_test_support::registry::Package; use cargo_test_support::str; use cargo_test_support::{basic_manifest, cross_compile, project}; use std::env; #[cargo_test] fn collision_dylib() { // Path dependencies don't include metadata hash in filename for dylibs. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "1.0.0" edition = "2015" [lib] crate-type = ["dylib"] "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "1.0.0" edition = "2015" [lib] crate-type = ["dylib"] name = "a" "#, ) .file("b/src/lib.rs", "") .build(); // `j=1` is required because on Windows you'll get an error due to // two processes writing to the file at the same time. p.cargo("build -j=1") .with_stderr_data(&format!("\ ... [WARNING] output filename collision. The lib target `a` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the lib target `a` in package `a v1.0.0 ([ROOT]/foo/a)`. Colliding filename is: [ROOT]/foo/target/debug/deps/{}a{} The targets should have unique names. Consider changing their names to be unique or compiling them separately. This may become a hard error in the future; see . ... ", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)) .run(); } #[cargo_test] fn collision_example() { // Examples in a workspace can easily collide. let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) .file("a/examples/ex1.rs", "fn main() {}") .file("b/Cargo.toml", &basic_manifest("b", "1.0.0")) .file("b/examples/ex1.rs", "fn main() {}") .build(); // `j=1` is required because on Windows you'll get an error due to // two processes writing to the file at the same time. p.cargo("build --examples -j=1") .with_stderr_data(str![[r#" ... [WARNING] output filename collision. The example target `ex1` in package `b v1.0.0 ([ROOT]/foo/b)` has the same output filename as the example target `ex1` in package `a v1.0.0 ([ROOT]/foo/a)`. Colliding filename is: [ROOT]/foo/target/debug/examples/ex1[EXE] The targets should have unique names. Consider changing their names to be unique or compiling them separately. This may become a hard error in the future; see . ... "#]]) .run(); } #[cargo_test] // See https://github.com/rust-lang/cargo/issues/7493 #[cfg_attr( any(target_env = "msvc", target_vendor = "apple"), ignore = "--artifact-dir and examples are currently broken on MSVC and apple" )] fn collision_export() { // `--artifact-dir` combines some things which can cause conflicts. let p = project() .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) .file("examples/foo.rs", "fn main() {}") .file("src/main.rs", "fn main() {}") .build(); // -j1 to avoid issues with two processes writing to the same file at the // same time. p.cargo("build -j1 --artifact-dir=out -Z unstable-options --bins --examples") .masquerade_as_nightly_cargo(&["artifact-dir"]) .with_stderr_data(str![[r#" [WARNING] `--artifact-dir` filename collision. The example target `foo` in package `foo v1.0.0 ([ROOT]/foo)` has the same output filename as the bin target `foo` in package `foo v1.0.0 ([ROOT]/foo)`. Colliding filename is: [ROOT]/foo/out/foo[EXE] The exported filenames should be unique. Consider changing their names to be unique or compiling them separately. This may become a hard error in the future; see . ... "#]]) .run(); } #[cargo_test] fn collision_doc() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] foo2 = { path = "foo2" } "#, ) .file("src/lib.rs", "") .file( "foo2/Cargo.toml", r#" [package] name = "foo2" version = "0.1.0" edition = "2015" [lib] name = "foo" "#, ) .file("foo2/src/lib.rs", "") .build(); p.cargo("doc -j=1") .with_stderr_data(str![[r#" ... [WARNING] output filename collision. The lib target `foo` in package `foo2 v0.1.0 ([ROOT]/foo/foo2)` has the same output filename as the lib target `foo` in package `foo v0.1.0 ([ROOT]/foo)`. Colliding filename is: [ROOT]/foo/target/doc/foo/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . ... "#]]) .run(); } #[cargo_test] fn collision_doc_multiple_versions() { // Multiple versions of the same package. Package::new("old-dep", "1.0.0").publish(); Package::new("bar", "1.0.0").dep("old-dep", "1.0").publish(); // Note that this removes "old-dep". Just checking what happens when there // are orphans. Package::new("bar", "2.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" bar2 = { package="bar", version="2.0" } "#, ) .file("src/lib.rs", "") .build(); // Should only document bar 2.0, should not document old-dep. p.cargo("doc") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 4 packages to latest compatible versions [ADDING] bar v1.0.0 (latest: v2.0.0) [DOWNLOADING] crates ... [DOWNLOADED] bar v2.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [DOWNLOADED] old-dep v1.0.0 (registry `dummy-registry`) [CHECKING] old-dep v1.0.0 [CHECKING] bar v2.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] bar v2.0.0 [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[allow(deprecated)] #[cargo_test] fn collision_doc_host_target_feature_split() { // Same dependency built twice due to different features. // // foo v0.1.0 // ├── common v1.0.0 // │ └── common-dep v1.0.0 // └── pm v0.1.0 (proc-macro) // └── common v1.0.0 // └── common-dep v1.0.0 // [build-dependencies] // └── common-dep v1.0.0 // // Here `common` and `common-dep` are built twice. `common-dep` has // different features for host versus target. Package::new("common-dep", "1.0.0") .feature("bdep-feat", &[]) .file( "src/lib.rs", r#" /// Some doc pub fn f() {} /// Another doc #[cfg(feature = "bdep-feat")] pub fn bdep_func() {} "#, ) .publish(); Package::new("common", "1.0.0") .dep("common-dep", "1.0") .file( "src/lib.rs", r#" /// Some doc pub fn f() {} "#, ) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" resolver = "2" [dependencies] pm = { path = "pm" } common = "1.0" [build-dependencies] common-dep = { version = "1.0", features = ["bdep-feat"] } "#, ) .file( "src/lib.rs", r#" /// Some doc pub fn f() {} "#, ) .file("build.rs", "fn main() {}") .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2018" [lib] proc-macro = true [dependencies] common = "1.0" "#, ) .file( "pm/src/lib.rs", r#" use proc_macro::TokenStream; /// Some doc #[proc_macro] pub fn pm(_input: TokenStream) -> TokenStream { "".parse().unwrap() } "#, ) .build(); // No warnings, no duplicates, common and common-dep only documented once. p.cargo("doc") // Cannot check full output due to https://github.com/rust-lang/cargo/issues/9076 .with_stderr_does_not_contain("[WARNING][..]") .run(); assert!(p.build_dir().join("doc/common_dep/fn.f.html").exists()); assert!(!p .build_dir() .join("doc/common_dep/fn.bdep_func.html") .exists()); assert!(p.build_dir().join("doc/common/fn.f.html").exists()); assert!(p.build_dir().join("doc/pm/macro.pm.html").exists()); assert!(p.build_dir().join("doc/foo/fn.f.html").exists()); } #[cargo_test] fn collision_doc_profile_split() { // Same dependency built twice due to different profile settings. Package::new("common", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] pm = { path = "pm" } common = "1.0" [profile.dev] opt-level = 2 "#, ) .file("src/lib.rs", "") .file( "pm/Cargo.toml", r#" [package] name = "pm" version = "0.1.0" edition = "2015" [dependencies] common = "1.0" [lib] proc-macro = true "#, ) .file("pm/src/lib.rs", "") .build(); // Just to verify that common is normally built twice. // This is unordered because in rare cases `pm` may start // building in-between the two `common`. p.cargo("build -v") .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] common v1.0.0 (registry `dummy-registry`) [COMPILING] common v1.0.0 [RUNNING] `rustc --crate-name common [..] [RUNNING] `rustc --crate-name common [..] [COMPILING] pm v0.1.0 ([ROOT]/foo/pm) [RUNNING] `rustc --crate-name pm [..] [COMPILING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo [..] [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s "#]] .unordered(), ) .run(); // Should only document common once, no warnings. p.cargo("doc") .with_stderr_data( str![[r#" [CHECKING] common v1.0.0 [DOCUMENTING] common v1.0.0 [DOCUMENTING] pm v0.1.0 ([ROOT]/foo/pm) [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [optimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_doc_sources() { // Different sources with the same package. Package::new("bar", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar = "1.0" bar2 = { path = "bar", package = "bar" } "#, ) .file("src/lib.rs", "") .file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("bar/src/lib.rs", "") .build(); p.cargo("doc -j=1") .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`) [WARNING] output filename collision. The lib target `bar` in package `bar v1.0.0` has the same output filename as the lib target `bar` in package `bar v1.0.0 ([ROOT]/foo/bar)`. Colliding filename is: [ROOT]/foo/target/doc/bar/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [CHECKING] bar v1.0.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v1.0.0 ([ROOT]/foo/bar) [DOCUMENTING] bar v1.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_doc_target() { // collision in doc with --target, doesn't fail due to orphans if cross_compile::disabled() { return; } Package::new("orphaned", "1.0.0").publish(); Package::new("bar", "1.0.0") .dep("orphaned", "1.0") .publish(); Package::new("bar", "2.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" edition = "2015" [dependencies] bar2 = { version = "2.0", package="bar" } bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("doc --target") .arg(cross_compile::alternate()) .with_stderr_data( str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 4 packages to latest compatible versions [ADDING] bar v1.0.0 (latest: v2.0.0) [DOWNLOADING] crates ... [DOWNLOADED] orphaned v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v2.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [CHECKING] orphaned v1.0.0 [DOCUMENTING] bar v2.0.0 [CHECKING] bar v2.0.0 [CHECKING] bar v1.0.0 [DOCUMENTING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/[ALT_TARGET]/doc/foo/index.html "#]] .unordered(), ) .run(); } #[cargo_test] fn collision_with_root() { // Check for a doc collision between a root package and a dependency. // In this case, `foo-macro` comes from both the workspace and crates.io. // This checks that the duplicate correction code doesn't choke on this // by removing the root unit. Package::new("foo-macro", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["abc", "foo-macro"] "#, ) .file( "abc/Cargo.toml", r#" [package] name = "abc" version = "1.0.0" edition = "2015" [dependencies] foo-macro = "1.0" "#, ) .file("abc/src/lib.rs", "") .file( "foo-macro/Cargo.toml", r#" [package] name = "foo-macro" version = "1.0.0" edition = "2015" [lib] proc-macro = true [dependencies] abc = {path="../abc"} "#, ) .file("foo-macro/src/lib.rs", "") .build(); p.cargo("doc -j=1") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 3 packages to latest compatible versions [DOWNLOADING] crates ... [DOWNLOADED] foo-macro v1.0.0 (registry `dummy-registry`) [WARNING] output filename collision. The lib target `foo_macro` in package `foo-macro v1.0.0` has the same output filename as the lib target `foo_macro` in package `foo-macro v1.0.0 ([ROOT]/foo/foo-macro)`. Colliding filename is: [ROOT]/foo/target/doc/foo_macro/index.html The targets should have unique names. This is a known bug where multiple crates with the same name use the same path; see . [CHECKING] foo-macro v1.0.0 [DOCUMENTING] foo-macro v1.0.0 [CHECKING] abc v1.0.0 ([ROOT]/foo/abc) [DOCUMENTING] foo-macro v1.0.0 ([ROOT]/foo/foo-macro) [DOCUMENTING] abc v1.0.0 ([ROOT]/foo/abc) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/abc/index.html and 1 other file "#]].unordered()) .run(); }