cargo/tests/testsuite/collisions.rs
Ed Page a6cf1be655 test: Switch from allow to expect deprecated
This caught a couple of lingering items.
2024-09-23 20:44:06 -05:00

595 lines
18 KiB
Rust

//! 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 <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_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 <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 = "--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 <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"
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 <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"
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] 3 packages to latest compatible versions
[ADDING] bar v1.0.0 (available: 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();
}
#[expect(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] 2 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] 2 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 <https://github.com/rust-lang/cargo/issues/6313>.
[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] 3 packages to latest compatible versions
[ADDING] bar v1.0.0 (available: 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] 1 package to latest compatible version
[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 <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 ([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();
}