summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/collisions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/collisions.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/collisions.rs550
1 files changed, 550 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/collisions.rs b/src/tools/cargo/tests/testsuite/collisions.rs
new file mode 100644
index 000000000..77e05dd9c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/collisions.rs
@@ -0,0 +1,550 @@
+//! 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::registry::Package;
+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"
+
+ [lib]
+ crate-type = ["dylib"]
+ "#,
+ )
+ .file("a/src/lib.rs", "")
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "1.0.0"
+
+ [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_contains(&format!("\
+[WARNING] output filename collision.
+The lib target `a` in package `b v1.0.0 ([..]/foo/b)` has the same output filename as the lib target `a` in package `a v1.0.0 ([..]/foo/a)`.
+Colliding filename is: [..]/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 <https://github.com/rust-lang/cargo/issues/6313>.
+", 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_contains("\
+[WARNING] output filename collision.
+The example target `ex1` in package `b v1.0.0 ([..]/foo/b)` has the same output filename as the example target `ex1` in package `a v1.0.0 ([..]/foo/a)`.
+Colliding filename is: [..]/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 <https://github.com/rust-lang/cargo/issues/6313>.
+")
+ .run();
+}
+
+#[cargo_test]
+// See https://github.com/rust-lang/cargo/issues/7493
+#[cfg_attr(
+ any(target_env = "msvc", target_vendor = "apple"),
+ ignore = "--out-dir and examples are currently broken on MSVC and apple"
+)]
+fn collision_export() {
+ // `--out-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 --out-dir=out -Z unstable-options --bins --examples")
+ .masquerade_as_nightly_cargo(&["out-dir"])
+ .with_stderr_contains("\
+[WARNING] `--out-dir` filename collision.
+The example target `foo` in package `foo v1.0.0 ([..]/foo)` has the same output filename as the bin target `foo` in package `foo v1.0.0 ([..]/foo)`.
+Colliding filename is: [..]/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 <https://github.com/rust-lang/cargo/issues/6313>.
+")
+ .run();
+}
+
+#[cargo_test]
+fn collision_doc() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ foo2 = { path = "foo2" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "foo2/Cargo.toml",
+ r#"
+ [package]
+ name = "foo2"
+ version = "0.1.0"
+
+ [lib]
+ name = "foo"
+ "#,
+ )
+ .file("foo2/src/lib.rs", "")
+ .build();
+
+ p.cargo("doc -j=1")
+ .with_stderr_contains(
+ "\
+[WARNING] output filename collision.
+The lib target `foo` in package `foo2 v0.1.0 ([..]/foo/foo2)` has the same output \
+filename as the lib target `foo` in package `foo v0.1.0 ([..]/foo)`.
+Colliding filename is: [..]/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 <https://github.com/rust-lang/cargo/issues/6313>.
+",
+ )
+ .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"
+
+ [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_unordered(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v2.0.0 [..]
+[DOWNLOADED] bar v1.0.0 [..]
+[DOWNLOADED] old-dep v1.0.0 [..]
+[CHECKING] old-dep v1.0.0
+[CHECKING] bar v2.0.0
+[CHECKING] bar v1.0.0
+[DOCUMENTING] bar v2.0.0
+[FINISHED] [..]
+[DOCUMENTING] foo v0.1.0 [..]
+",
+ )
+ .run();
+}
+
+#[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"
+ 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"
+
+ [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"
+
+ [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_unordered(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] common v1.0.0 [..]
+[COMPILING] common v1.0.0
+[RUNNING] `rustc --crate-name common [..]
+[RUNNING] `rustc --crate-name common [..]
+[COMPILING] pm v0.1.0 [..]
+[RUNNING] `rustc --crate-name pm [..]
+[COMPILING] foo v0.1.0 [..]
+[RUNNING] `rustc --crate-name foo [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+
+ // Should only document common once, no warnings.
+ p.cargo("doc")
+ .with_stderr_unordered(
+ "\
+[CHECKING] common v1.0.0
+[DOCUMENTING] common v1.0.0
+[DOCUMENTING] pm v0.1.0 [..]
+[DOCUMENTING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .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"
+
+ [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_unordered(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v1.0.0 [..]
+[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 ([..]/foo/bar)`.
+Colliding filename is: [..]/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 <https://github.com/rust-lang/cargo/issues/6313>.
+[CHECKING] bar v1.0.0 [..]
+[DOCUMENTING] bar v1.0.0 [..]
+[DOCUMENTING] bar v1.0.0
+[CHECKING] bar v1.0.0
+[DOCUMENTING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .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"
+
+ [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_unordered(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] orphaned v1.0.0 [..]
+[DOWNLOADED] bar v2.0.0 [..]
+[DOWNLOADED] bar v1.0.0 [..]
+[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 [..]
+[FINISHED] [..]
+",
+ )
+ .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"
+
+ [dependencies]
+ foo-macro = "1.0"
+ "#,
+ )
+ .file("abc/src/lib.rs", "")
+ .file(
+ "foo-macro/Cargo.toml",
+ r#"
+ [package]
+ name = "foo-macro"
+ version = "1.0.0"
+
+ [lib]
+ proc-macro = true
+
+ [dependencies]
+ abc = {path="../abc"}
+ "#,
+ )
+ .file("foo-macro/src/lib.rs", "")
+ .build();
+
+ p.cargo("doc -j=1")
+ .with_stderr_unordered("\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] foo-macro v1.0.0 [..]
+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 [..]`.
+Colliding filename is: [CWD]/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 <https://github.com/rust-lang/cargo/issues/6313>.
+[CHECKING] foo-macro v1.0.0
+[DOCUMENTING] foo-macro v1.0.0
+[CHECKING] abc v1.0.0 [..]
+[DOCUMENTING] foo-macro v1.0.0 [..]
+[DOCUMENTING] abc v1.0.0 [..]
+[FINISHED] [..]
+")
+ .run();
+}