Skip to content

Commit b4c4028

Browse files
committed
Run rustdoc doctests relative to the workspace
By doing so, rustdoc will also emit workspace-relative filenames for the doctests. This was first landed in #8954 but later backed out in #8996 because it changed the CWD of rustdoc test invocations. The second try relies on the new `--test-run-directory` rustdoc option which was added in rust-lang/rust#81264 to explicitly control the rustdoc test cwd. fixes #8993
1 parent 7442c14 commit b4c4028

File tree

12 files changed

+148
-53
lines changed

12 files changed

+148
-53
lines changed

src/cargo/core/compiler/fingerprint.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ use crate::util;
334334
use crate::util::errors::{CargoResult, CargoResultExt};
335335
use crate::util::interning::InternedString;
336336
use crate::util::paths;
337-
use crate::util::{internal, profile, ProcessBuilder};
337+
use crate::util::{internal, path_args, profile, ProcessBuilder};
338338

339339
use super::custom_build::BuildDeps;
340340
use super::job::{Job, Work};
@@ -1324,7 +1324,7 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
13241324
profile: profile_hash,
13251325
// Note that .0 is hashed here, not .1 which is the cwd. That doesn't
13261326
// actually affect the output artifact so there's no need to hash it.
1327-
path: util::hash_u64(super::path_args(cx.bcx, unit).0),
1327+
path: util::hash_u64(path_args(cx.bcx.ws, unit).0),
13281328
features: format!("{:?}", unit.features),
13291329
deps,
13301330
local: Mutex::new(local),

src/cargo/core/compiler/mod.rs

+3-38
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ use crate::util::errors::{self, CargoResult, CargoResultExt, ProcessError, Verbo
5757
use crate::util::interning::InternedString;
5858
use crate::util::machine_message::Message;
5959
use crate::util::{self, machine_message, ProcessBuilder};
60-
use crate::util::{internal, join_paths, paths, profile};
60+
use crate::util::{add_path_args, internal, join_paths, paths, profile};
6161

6262
const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
6363

@@ -582,7 +582,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
582582
let mut rustdoc = cx.compilation.rustdoc_process(unit, None)?;
583583
rustdoc.inherit_jobserver(&cx.jobserver);
584584
rustdoc.arg("--crate-name").arg(&unit.target.crate_name());
585-
add_path_args(bcx, unit, &mut rustdoc);
585+
add_path_args(bcx.ws, unit, &mut rustdoc);
586586
add_cap_lints(bcx, unit, &mut rustdoc);
587587

588588
if let CompileKind::Target(target) = unit.kind {
@@ -662,41 +662,6 @@ fn append_crate_version_flag(unit: &Unit, rustdoc: &mut ProcessBuilder) {
662662
.arg(unit.pkg.version().to_string());
663663
}
664664

665-
// The path that we pass to rustc is actually fairly important because it will
666-
// show up in error messages (important for readability), debug information
667-
// (important for caching), etc. As a result we need to be pretty careful how we
668-
// actually invoke rustc.
669-
//
670-
// In general users don't expect `cargo build` to cause rebuilds if you change
671-
// directories. That could be if you just change directories in the package or
672-
// if you literally move the whole package wholesale to a new directory. As a
673-
// result we mostly don't factor in `cwd` to this calculation. Instead we try to
674-
// track the workspace as much as possible and we update the current directory
675-
// of rustc/rustdoc where appropriate.
676-
//
677-
// The first returned value here is the argument to pass to rustc, and the
678-
// second is the cwd that rustc should operate in.
679-
fn path_args(bcx: &BuildContext<'_, '_>, unit: &Unit) -> (PathBuf, PathBuf) {
680-
let ws_root = bcx.ws.root();
681-
let src = match unit.target.src_path() {
682-
TargetSourcePath::Path(path) => path.to_path_buf(),
683-
TargetSourcePath::Metabuild => unit.pkg.manifest().metabuild_path(bcx.ws.target_dir()),
684-
};
685-
assert!(src.is_absolute());
686-
if unit.pkg.package_id().source_id().is_path() {
687-
if let Ok(path) = src.strip_prefix(ws_root) {
688-
return (path.to_path_buf(), ws_root.to_path_buf());
689-
}
690-
}
691-
(src, unit.pkg.root().to_path_buf())
692-
}
693-
694-
fn add_path_args(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
695-
let (arg, cwd) = path_args(bcx, unit);
696-
cmd.arg(arg);
697-
cmd.cwd(cwd);
698-
}
699-
700665
fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit, cmd: &mut ProcessBuilder) {
701666
// If this is an upstream dep we don't want warnings from, turn off all
702667
// lints.
@@ -786,7 +751,7 @@ fn build_base_args(
786751
cmd.arg(format!("--edition={}", edition));
787752
}
788753

789-
add_path_args(bcx, unit, cmd);
754+
add_path_args(bcx.ws, unit, cmd);
790755
add_error_format_and_color(cx, cmd, cx.rmeta_required(unit));
791756

792757
if !test {

src/cargo/core/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ pub struct CliUnstable {
433433
pub build_std_features: Option<Vec<String>>,
434434
pub timings: Option<Vec<String>>,
435435
pub doctest_xcompile: bool,
436+
pub doctest_in_workspace: bool,
436437
pub panic_abort_tests: bool,
437438
pub jobserver_per_rustc: bool,
438439
pub features: Option<Vec<String>>,
@@ -596,6 +597,7 @@ impl CliUnstable {
596597
"build-std-features" => self.build_std_features = Some(parse_features(v)),
597598
"timings" => self.timings = Some(parse_timings(v)),
598599
"doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
600+
"doctest-in-workspace" => self.doctest_in_workspace = parse_empty(k, v)?,
599601
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
600602
"jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?,
601603
"features" => {

src/cargo/ops/cargo_test.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::core::shell::Verbosity;
55
use crate::core::Workspace;
66
use crate::ops;
77
use crate::util::errors::CargoResult;
8-
use crate::util::{CargoTestError, Config, ProcessError, Test};
8+
use crate::util::{add_path_args, CargoTestError, Config, ProcessError, Test};
99

1010
pub struct TestOptions {
1111
pub compile_opts: ops::CompileOptions,
@@ -30,7 +30,7 @@ pub fn run_tests(
3030
return Ok(Some(CargoTestError::new(test, errors)));
3131
}
3232

33-
let (doctest, docerrors) = run_doc_tests(ws.config(), options, test_args, &compilation)?;
33+
let (doctest, docerrors) = run_doc_tests(ws, options, test_args, &compilation)?;
3434
let test = if docerrors.is_empty() { test } else { doctest };
3535
errors.extend(docerrors);
3636
if errors.is_empty() {
@@ -136,13 +136,15 @@ fn run_unit_tests(
136136
}
137137

138138
fn run_doc_tests(
139-
config: &Config,
139+
ws: &Workspace<'_>,
140140
options: &TestOptions,
141141
test_args: &[&str],
142142
compilation: &Compilation<'_>,
143143
) -> CargoResult<(Test, Vec<ProcessError>)> {
144+
let config = ws.config();
144145
let mut errors = Vec::new();
145146
let doctest_xcompile = config.cli_unstable().doctest_xcompile;
147+
let doctest_in_workspace = config.cli_unstable().doctest_in_workspace;
146148

147149
for doctest_info in &compilation.to_doc_test {
148150
let Doctest {
@@ -167,10 +169,18 @@ fn run_doc_tests(
167169

168170
config.shell().status("Doc-tests", unit.target.name())?;
169171
let mut p = compilation.rustdoc_process(unit, *script_meta)?;
170-
p.arg("--test")
171-
.arg(unit.target.src_path().path().unwrap())
172-
.arg("--crate-name")
173-
.arg(&unit.target.crate_name());
172+
p.arg("--crate-name").arg(&unit.target.crate_name());
173+
p.arg("--test");
174+
175+
if doctest_in_workspace {
176+
add_path_args(ws, unit, &mut p);
177+
// FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option
178+
p.arg("-Z").arg("unstable-options");
179+
p.arg("--test-run-directory")
180+
.arg(unit.pkg.root().to_path_buf());
181+
} else {
182+
p.arg(unit.target.src_path().path().unwrap());
183+
}
174184

175185
if doctest_xcompile {
176186
if let CompileKind::Target(target) = unit.kind {

src/cargo/util/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ pub use self::sha256::Sha256;
2727
pub use self::to_semver::ToSemver;
2828
pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
2929
pub use self::workspace::{
30-
print_available_benches, print_available_binaries, print_available_examples,
31-
print_available_packages, print_available_tests,
30+
add_path_args, path_args, print_available_benches, print_available_binaries,
31+
print_available_examples, print_available_packages, print_available_tests,
3232
};
3333

3434
mod canonical_url;

src/cargo/util/workspace.rs

+39
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use super::ProcessBuilder;
2+
use crate::core::compiler::Unit;
3+
use crate::core::manifest::TargetSourcePath;
14
use crate::core::{Target, Workspace};
25
use crate::ops::CompileOptions;
36
use crate::util::CargoResult;
47
use anyhow::bail;
58
use std::fmt::Write;
9+
use std::path::PathBuf;
610

711
fn get_available_targets<'a>(
812
filter_fn: fn(&Target) -> bool,
@@ -89,3 +93,38 @@ pub fn print_available_benches(ws: &Workspace<'_>, options: &CompileOptions) ->
8993
pub fn print_available_tests(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
9094
print_available_targets(Target::is_test, ws, options, "--test", "tests")
9195
}
96+
97+
/// The path that we pass to rustc is actually fairly important because it will
98+
/// show up in error messages (important for readability), debug information
99+
/// (important for caching), etc. As a result we need to be pretty careful how we
100+
/// actually invoke rustc.
101+
///
102+
/// In general users don't expect `cargo build` to cause rebuilds if you change
103+
/// directories. That could be if you just change directories in the package or
104+
/// if you literally move the whole package wholesale to a new directory. As a
105+
/// result we mostly don't factor in `cwd` to this calculation. Instead we try to
106+
/// track the workspace as much as possible and we update the current directory
107+
/// of rustc/rustdoc where appropriate.
108+
///
109+
/// The first returned value here is the argument to pass to rustc, and the
110+
/// second is the cwd that rustc should operate in.
111+
pub fn path_args(ws: &Workspace<'_>, unit: &Unit) -> (PathBuf, PathBuf) {
112+
let ws_root = ws.root();
113+
let src = match unit.target.src_path() {
114+
TargetSourcePath::Path(path) => path.to_path_buf(),
115+
TargetSourcePath::Metabuild => unit.pkg.manifest().metabuild_path(ws.target_dir()),
116+
};
117+
assert!(src.is_absolute());
118+
if unit.pkg.package_id().source_id().is_path() {
119+
if let Ok(path) = src.strip_prefix(ws_root) {
120+
return (path.to_path_buf(), ws_root.to_path_buf());
121+
}
122+
}
123+
(src, unit.pkg.root().to_path_buf())
124+
}
125+
126+
pub fn add_path_args(ws: &Workspace<'_>, unit: &Unit, cmd: &mut ProcessBuilder) {
127+
let (arg, cwd) = path_args(ws, unit);
128+
cmd.arg(arg);
129+
cmd.cwd(cwd);
130+
}

tests/testsuite/build_script.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2753,7 +2753,7 @@ fn doctest_receives_build_link_args() {
27532753

27542754
p.cargo("test -v")
27552755
.with_stderr_contains(
2756-
"[RUNNING] `rustdoc [..]--test [..] --crate-name foo [..]-L native=bar[..]`",
2756+
"[RUNNING] `rustdoc [..]--crate-name foo --test [..]-L native=bar[..]`",
27572757
)
27582758
.run();
27592759
}

tests/testsuite/cross_compile.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,7 @@ fn doctest_xcompile_linker() {
11091109
.masquerade_as_nightly_cargo()
11101110
.with_stderr_contains(&format!(
11111111
"\
1112-
[RUNNING] `rustdoc --crate-type lib --test [..]\
1112+
[RUNNING] `rustdoc --crate-type lib --crate-name foo --test [..]\
11131113
--target {target} [..] -C linker=my-linker-tool[..]
11141114
",
11151115
target = target,

tests/testsuite/doc.rs

+76
Original file line numberDiff line numberDiff line change
@@ -1640,6 +1640,82 @@ fn crate_versions_flag_is_overridden() {
16401640
asserts(output_documentation());
16411641
}
16421642

1643+
#[cargo_test]
1644+
fn doc_test_in_workspace() {
1645+
if !is_nightly() {
1646+
// -Zdoctest-in-workspace is unstable
1647+
return;
1648+
}
1649+
1650+
let p = project()
1651+
.file(
1652+
"Cargo.toml",
1653+
r#"
1654+
[workspace]
1655+
members = [
1656+
"crate-a",
1657+
"crate-b",
1658+
]
1659+
"#,
1660+
)
1661+
.file(
1662+
"crate-a/Cargo.toml",
1663+
r#"
1664+
[project]
1665+
name = "crate-a"
1666+
version = "0.1.0"
1667+
"#,
1668+
)
1669+
.file(
1670+
"crate-a/src/lib.rs",
1671+
"\
1672+
//! ```
1673+
//! assert_eq!(1, 1);
1674+
//! ```
1675+
",
1676+
)
1677+
.file(
1678+
"crate-b/Cargo.toml",
1679+
r#"
1680+
[project]
1681+
name = "crate-b"
1682+
version = "0.1.0"
1683+
"#,
1684+
)
1685+
.file(
1686+
"crate-b/src/lib.rs",
1687+
"\
1688+
//! ```
1689+
//! assert_eq!(1, 1);
1690+
//! ```
1691+
",
1692+
)
1693+
.build();
1694+
p.cargo("test -Zdoctest-in-workspace --doc -vv")
1695+
.masquerade_as_nightly_cargo()
1696+
.with_stderr_contains("[DOCTEST] crate-a")
1697+
.with_stdout_contains(
1698+
"
1699+
running 1 test
1700+
test crate-a/src/lib.rs - (line 1) ... ok
1701+
1702+
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
1703+
1704+
",
1705+
)
1706+
.with_stderr_contains("[DOCTEST] crate-b")
1707+
.with_stdout_contains(
1708+
"
1709+
running 1 test
1710+
test crate-b/src/lib.rs - (line 1) ... ok
1711+
1712+
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..]
1713+
1714+
",
1715+
)
1716+
.run();
1717+
}
1718+
16431719
#[cargo_test]
16441720
fn doc_fingerprint_is_versioning_consistent() {
16451721
// Random rustc verbose version

tests/testsuite/lto.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ fn cdylib_and_rlib() {
526526
[RUNNING] [..]target/release/deps/bar-[..]
527527
[RUNNING] [..]target/release/deps/b-[..]
528528
[DOCTEST] bar
529-
[RUNNING] `rustdoc --crate-type cdylib --crate-type rlib --test [..]-C embed-bitcode=no[..]
529+
[RUNNING] `rustdoc --crate-type cdylib --crate-type rlib --crate-name bar --test [..]-C embed-bitcode=no[..]
530530
",
531531
)
532532
.run();

tests/testsuite/rename_deps.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ fn can_run_doc_tests() {
266266
.with_stderr_contains(
267267
"\
268268
[DOCTEST] foo
269-
[RUNNING] `rustdoc [..]--test [CWD]/src/lib.rs \
269+
[RUNNING] `rustdoc [..]--test [..]src/lib.rs \
270270
[..] \
271271
--extern bar=[CWD]/target/debug/deps/libbar-[..].rlib \
272272
--extern baz=[CWD]/target/debug/deps/libbar-[..].rlib \

tests/testsuite/test.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4256,6 +4256,7 @@ fn test_workspaces_cwd() {
42564256
r#"
42574257
//! ```
42584258
//! assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap());
4259+
//! assert_eq!("{expected}", include_str!("../file.txt"));
42594260
//! assert_eq!(
42604261
//! std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")),
42614262
//! std::env::current_dir().unwrap(),
@@ -4265,6 +4266,7 @@ fn test_workspaces_cwd() {
42654266
#[test]
42664267
fn test_unit_{expected}_cwd() {{
42674268
assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap());
4269+
assert_eq!("{expected}", include_str!("../file.txt"));
42684270
assert_eq!(
42694271
std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")),
42704272
std::env::current_dir().unwrap(),
@@ -4280,6 +4282,7 @@ fn test_workspaces_cwd() {
42804282
#[test]
42814283
fn test_integration_{expected}_cwd() {{
42824284
assert_eq!("{expected}", std::fs::read_to_string("file.txt").unwrap());
4285+
assert_eq!("{expected}", include_str!("../file.txt"));
42834286
assert_eq!(
42844287
std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR")),
42854288
std::env::current_dir().unwrap(),

0 commit comments

Comments
 (0)