diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index ffd3881dc57..b51d61f2c40 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -55,8 +55,8 @@ pub struct Compilation<'cfg> { /// An array of all cdylibs created. pub cdylibs: Vec, - /// The crate names of the root units specified on the command-line. - pub root_crate_names: Vec, + /// The crate names and kinds of the root units specified on the command-line. + pub root_crate_names: Vec<(String, CompileKind)>, /// All directories for the output of native build commands. /// diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 3f13f086c95..179c642144f 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -321,9 +321,12 @@ 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.compilation.root_crate_names.extend( + self.bcx + .roots + .iter() + .map(|u| (u.target.crate_name(), u.kind.clone())), + ); self.record_units_requiring_metadata(); diff --git a/src/cargo/ops/cargo_doc.rs b/src/cargo/ops/cargo_doc.rs index afa6ac327d8..168d78c2eff 100644 --- a/src/cargo/ops/cargo_doc.rs +++ b/src/cargo/ops/cargo_doc.rs @@ -20,11 +20,21 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> { let compilation = ops::compile(ws, &options.compile_opts)?; if options.open_result { - let name = &compilation + // The open behavior is as follows: + // cargo doc --open: + // - Pick the first root unit that was built for host. + // - If none found, pick the first one(whatever it's target is). + // cargo doc --target TARGET --open: + // - Pick the first root unit for the given target. + // - If none found, pick the first one(whatever it's target is). + let request_kind = options.compile_opts.build_config.single_requested_kind()?; + let (name, kind) = &compilation .root_crate_names - .get(0) + .iter() + .find(|(_, kind)| *kind == request_kind) + .or_else(|| compilation.root_crate_names.get(0)) .ok_or_else(|| anyhow::anyhow!("no crates with documentation"))?; - let kind = options.compile_opts.build_config.single_requested_kind()?; + let path = compilation.root_output[&kind] .with_file_name("doc") .join(&name) diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs index 739bcf3765c..be5e9b2fa7b 100644 --- a/tests/testsuite/doc.rs +++ b/tests/testsuite/doc.rs @@ -3,7 +3,7 @@ use cargo::core::compiler::RustDocFingerprint; use cargo_test_support::paths::CargoPathExt; use cargo_test_support::registry::Package; -use cargo_test_support::{basic_lib_manifest, basic_manifest, git, project}; +use cargo_test_support::{basic_lib_manifest, basic_manifest, cross_compile, git, project}; use cargo_test_support::{rustc_host, symlink_supported, tools}; use std::fs; use std::str; @@ -777,6 +777,29 @@ fn doc_target() { .is_file()); } +#[cargo_test] +fn doc_and_open() { + let p = project() + .file( + "src/lib.rs", + " + /// test + pub fn foo() {} + ", + ) + .build(); + + p.cargo("doc --verbose --open --target") + .arg(rustc_host()) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting foo v0.0.1 ([..])") + .with_stderr_contains(&format!( + "[..] Opening [..]/target/{}/doc/foo/index.html", + rustc_host() + )) + .run(); +} + #[cargo_test] fn target_specific_not_documented() { let p = project() @@ -1206,6 +1229,90 @@ fn doc_virtual_manifest_one_project() { .run(); } +#[cargo_test] +fn doc_and_open_virtual_manifest_one_project() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "bar" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc -p bar --open") + .masquerade_as_nightly_cargo(&["per-package-target"]) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains(&format!( + "[..] Opening [..]/target/{}/doc/bar/index.html", + target + )) + .run(); +} + +#[cargo_test] +fn doc_and_open_virtual_manifest_two_projects() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "bar" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc -p foo -p bar --open") + .masquerade_as_nightly_cargo(&["per-package-target"]) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains("[..] Opening [..]/foo/index.html") + .run(); +} + #[cargo_test] fn doc_virtual_manifest_glob() { let p = project() @@ -1306,6 +1413,210 @@ fn doc_workspace_open_help_message() { .run(); } +#[cargo_test] +fn doc_workspace_open_first_one_built_for_host() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "bar" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace --open") + .masquerade_as_nightly_cargo(&["per-package-target"]) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains("[..] Opening [..]/foo/index.html") + .run(); +} + +#[cargo_test] +fn doc_workspace_open_first_one_when_no_one_built_for_host() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "foo" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "bar" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace --open") + .masquerade_as_nightly_cargo(&["per-package-target"]) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains(&format!( + "[..] Opening [..]/target/{}/doc/bar/index.html", + target + )) + .run(); +} + +#[cargo_test] +fn doc_workspace_open_first_one_built_for_target() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "bar" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("doc --workspace --open --target") + .arg(target) + .masquerade_as_nightly_cargo(&["per-package-target"]) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains(&format!( + "[..] Opening [..]/target/{}/doc/bar/index.html", + target + )) + .run(); +} + +#[cargo_test] +fn doc_workspace_open_first_one_when_no_one_built_for_target() { + if cross_compile::disabled() { + return; + } + let target = cross_compile::alternate(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "foo" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("foo/src/lib.rs", "") + .file( + "bar/Cargo.toml", + &format!( + r#" + cargo-features = ["per-package-target"] + [package] + name = "bar" + version = "0.1.0" + forced-target = "{}" + "#, + target + ), + ) + .file("bar/src/lib.rs", "") + .build(); + + // We don't build for host, so we should open the first one built for TARGET. + let host_target = rustc_host(); + p.cargo("doc --workspace --open --target") + .arg(host_target) + .masquerade_as_nightly_cargo(&["per-package-target"]) + .env("BROWSER", tools::echo()) + .with_stderr_contains("[..] Documenting bar v0.1.0 ([..])") + .with_stderr_contains("[..] Documenting foo v0.1.0 ([..])") + .with_stderr_contains(&format!( + "[..] Opening [..]/target/{}/doc/bar/index.html", + target + )) + .run(); +} + #[cargo_test(nightly, reason = "-Zextern-html-root-url is unstable")] fn doc_extern_map_local() { let p = project()