cargo/tests/testsuite/collisions.rs
Ed Page 293f2250d6 feat(doc): Print the generated docs links
I've wanted something like this myself.  I dislike using `--open`
because I tend to move up to re-run my `cargo doc` run but then have to
edit it to remove `--open`.
Also makes it annoying when opening docs when `cargo doc` is wrapped by
a tool like `make`.

This was previously attempted in #5592:
- Unlike the request in #5562, this aligns with #5592 in always printing
  rather than using a flag as this seems generally useful
- Unlike #5592, this prints as an alternative to "Opening" to keep
  things light
- Unlike #5592, this prints afterwards as the link is only valid then

Fixes #5562
2023-10-19 10:51:56 -05:00

556 lines
16 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::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 [..]
[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.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] [..]
[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.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] [..]
[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.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] [..]
[GENERATED] [CWD]/target/[..]/doc/foo/index.html
",
)
.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] [..]
[GENERATED] [CWD]/target/doc/abc/index.html
[GENERATED] [CWD]/target/doc/foo_macro/index.html
")
.run();
}