Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate doc collision detection. #9526

Merged
merged 1 commit into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/cargo/core/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ pub struct Compilation<'cfg> {
/// An array of all cdylibs created.
pub cdylibs: Vec<UnitOutput>,

/// The crate names of the root units specified on the command-line.
pub root_crate_names: Vec<String>,

/// All directories for the output of native build commands.
///
/// This is currently used to drive some entries which are added to the
Expand Down Expand Up @@ -136,6 +139,7 @@ impl<'cfg> Compilation<'cfg> {
tests: Vec::new(),
binaries: Vec::new(),
cdylibs: Vec::new(),
root_crate_names: Vec::new(),
extra_env: HashMap::new(),
to_doc_test: Vec::new(),
config: bcx.config,
Expand Down
26 changes: 25 additions & 1 deletion src/cargo/core/compiler/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::{BTreeSet, HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};

use anyhow::Context as _;
use anyhow::{bail, Context as _};
use filetime::FileTime;
use jobserver::Client;

Expand Down Expand Up @@ -309,6 +309,9 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
}
self.primary_packages
.extend(self.bcx.roots.iter().map(|u| u.pkg.package_id()));
self.compilation
.root_crate_names
.extend(self.bcx.roots.iter().map(|u| u.target.crate_name()));

self.record_units_requiring_metadata();

Expand Down Expand Up @@ -480,6 +483,22 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
}
};

fn doc_collision_error(unit: &Unit, other_unit: &Unit) -> CargoResult<()> {
bail!(
"document output filename collision\n\
The {} `{}` in package `{}` has the same name as the {} `{}` in package `{}`.\n\
Only one may be documented at once since they output to the same path.\n\
Consider documenting only one, renaming one, \
or marking one with `doc = false` in Cargo.toml.",
unit.target.kind().description(),
unit.target.name(),
unit.pkg,
other_unit.target.kind().description(),
other_unit.target.name(),
other_unit.pkg,
);
}

let mut keys = self
.bcx
.unit_graph
Expand All @@ -494,6 +513,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
if unit.mode.is_doc() {
// See https://github.com/rust-lang/rust/issues/56169
// and https://github.com/rust-lang/rust/issues/61378
if self.is_primary_package(unit) {
// This has been an error since before 1.0, so it
// is not a warning like the other situations.
doc_collision_error(unit, other_unit)?;
}
report_collision(unit, other_unit, &output.path, rustdoc_suggestion)?;
} else {
report_collision(unit, other_unit, &output.path, suggestion)?;
Expand Down
8 changes: 7 additions & 1 deletion src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,9 @@ fn generate_targets(
// It is computed by the set of enabled features for the package plus
// every enabled feature of every enabled dependency.
let mut features_map = HashMap::new();
// This needs to be a set to de-duplicate units. Due to the way the
// targets are filtered, it is possible to have duplicate proposals for
// the same thing.
let mut units = HashSet::new();
for Proposal {
pkg,
Expand Down Expand Up @@ -1136,7 +1139,10 @@ fn generate_targets(
}
// else, silently skip target.
}
Ok(units.into_iter().collect())
let mut units: Vec<_> = units.into_iter().collect();
// Keep the roots in a consistent order, which helps with checking test output.
units.sort_unstable();
Ok(units)
}

/// Warns if a target's required-features references a feature that doesn't exist.
Expand Down
64 changes: 5 additions & 59 deletions src/cargo/ops/cargo_doc.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::core::resolver::HasDevUnits;
use crate::core::{Shell, Workspace};
use crate::ops;
use crate::util::config::PathAndArgs;
use crate::util::CargoResult;
use crate::{core::compiler::RustcTargetData, util::config::PathAndArgs};
use serde::Deserialize;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::{collections::HashMap, path::PathBuf};

/// Strongly typed options for the `cargo doc` command.
#[derive(Debug)]
Expand All @@ -26,64 +25,11 @@ struct CargoDocConfig {

/// Main method for `cargo doc`.
pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
let specs = options.compile_opts.spec.to_package_id_specs(ws)?;
let target_data = RustcTargetData::new(ws, &options.compile_opts.build_config.requested_kinds)?;
let ws_resolve = ops::resolve_ws_with_opts(
ws,
&target_data,
&options.compile_opts.build_config.requested_kinds,
&options.compile_opts.cli_features,
&specs,
HasDevUnits::No,
crate::core::resolver::features::ForceAllTargets::No,
)?;

let ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?;
let pkgs = ws_resolve.pkg_set.get_many(ids)?;

let mut lib_names = HashMap::new();
let mut bin_names = HashMap::new();
let mut names = Vec::new();
for package in &pkgs {
for target in package.targets().iter().filter(|t| t.documented()) {
if target.is_lib() {
if let Some(prev) = lib_names.insert(target.crate_name(), package) {
anyhow::bail!(
"The library `{}` is specified by packages `{}` and \
`{}` but can only be documented once. Consider renaming \
or marking one of the targets as `doc = false`.",
target.crate_name(),
prev,
package
);
}
} else if let Some(prev) = bin_names.insert(target.crate_name(), package) {
anyhow::bail!(
"The binary `{}` is specified by packages `{}` and \
`{}` but can be documented only once. Consider renaming \
or marking one of the targets as `doc = false`.",
target.crate_name(),
prev,
package
);
}
names.push(target.crate_name());
}
}

let open_kind = if options.open_result {
Some(options.compile_opts.build_config.single_requested_kind()?)
} else {
None
};

let compilation = ops::compile(ws, &options.compile_opts)?;

if let Some(kind) = open_kind {
let name = match names.first() {
Some(s) => s.to_string(),
None => return Ok(()),
};
if options.open_result {
let name = &compilation.root_crate_names[0];
let kind = options.compile_opts.build_config.single_requested_kind()?;
let path = compilation.root_output[&kind]
.with_file_name("doc")
.join(&name)
Expand Down
54 changes: 33 additions & 21 deletions tests/testsuite/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,15 @@ fn doc_multiple_targets_same_name_lib() {

p.cargo("doc --workspace")
.with_status(101)
.with_stderr_contains("[..] library `foo_lib` is specified [..]")
.with_stderr_contains("[..] `foo v0.1.0[..]` [..]")
.with_stderr_contains("[..] `bar v0.1.0[..]` [..]")
.with_stderr(
"\
error: document output filename collision
The lib `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as \
the lib `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)`.
Only one may be documented at once since they output to the same path.
Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml.
",
)
.run();
}

Expand Down Expand Up @@ -265,13 +271,17 @@ fn doc_multiple_targets_same_name() {
.build();

p.cargo("doc --workspace")
.with_stderr_contains("[DOCUMENTING] foo v0.1.0 ([CWD]/foo)")
.with_stderr_contains("[DOCUMENTING] bar v0.1.0 ([CWD]/bar)")
.with_stderr_contains("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
.with_status(101)
.with_stderr(
"\
error: document output filename collision
The bin `foo_lib` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as \
the lib `foo_lib` in package `bar v0.1.0 ([ROOT]/foo/bar)`.
Only one may be documented at once since they output to the same path.
Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml.
",
)
.run();
assert!(p.root().join("target/doc").is_dir());
let doc_file = p.root().join("target/doc/foo_lib/index.html");
assert!(doc_file.is_file());
}

#[cargo_test]
Expand All @@ -290,29 +300,31 @@ fn doc_multiple_targets_same_name_bin() {
[package]
name = "foo"
version = "0.1.0"
[[bin]]
name = "foo-cli"
"#,
)
.file("foo/src/foo-cli.rs", "")
.file("foo/src/bin/foo-cli.rs", "")
.file(
"bar/Cargo.toml",
r#"
[package]
name = "bar"
version = "0.1.0"
[[bin]]
name = "foo-cli"
"#,
)
.file("bar/src/foo-cli.rs", "")
.file("bar/src/bin/foo-cli.rs", "")
.build();

p.cargo("doc --workspace")
.with_status(101)
.with_stderr_contains("[..] binary `foo_cli` is specified [..]")
.with_stderr_contains("[..] `foo v0.1.0[..]` [..]")
.with_stderr_contains("[..] `bar v0.1.0[..]` [..]")
.with_stderr(
"\
error: document output filename collision
The bin `foo-cli` in package `foo v0.1.0 ([ROOT]/foo/foo)` has the same name as \
the bin `foo-cli` in package `bar v0.1.0 ([ROOT]/foo/bar)`.
Only one may be documented at once since they output to the same path.
Consider documenting only one, renaming one, or marking one with `doc = false` in Cargo.toml.
",
)
.run();
}

Expand Down Expand Up @@ -1152,7 +1164,7 @@ fn doc_workspace_open_help_message() {
.env("BROWSER", "echo")
.with_stderr_contains("[..] Documenting bar v0.1.0 ([..])")
.with_stderr_contains("[..] Documenting foo v0.1.0 ([..])")
.with_stderr_contains("[..] Opening [..]/foo/index.html")
.with_stderr_contains("[..] Opening [..]/bar/index.html")
.run();
}

Expand Down Expand Up @@ -1378,7 +1390,7 @@ fn doc_private_ws() {
.file("a/src/lib.rs", "fn p() {}")
.file("b/Cargo.toml", &basic_manifest("b", "0.0.1"))
.file("b/src/lib.rs", "fn p2() {}")
.file("b/src/main.rs", "fn main() {}")
.file("b/src/bin/b-cli.rs", "fn main() {}")
.build();
p.cargo("doc --workspace --bins --lib --document-private-items -v")
.with_stderr_contains(
Expand All @@ -1388,7 +1400,7 @@ fn doc_private_ws() {
"[RUNNING] `rustdoc [..] b/src/lib.rs [..]--document-private-items[..]",
)
.with_stderr_contains(
"[RUNNING] `rustdoc [..] b/src/main.rs [..]--document-private-items[..]",
"[RUNNING] `rustdoc [..] b/src/bin/b-cli.rs [..]--document-private-items[..]",
)
.run();
}
Expand Down