Skip to content

Commit f613dc1

Browse files
committed
Auto merge of #56189 - rep-nop:keep_doc_test_executable, r=QuietMisdreavus
rustdoc: Add option to persist doc test executables Fixes #37048. This is the initial version of the code so the doctest executables can be used for stuff like code coverage (specifically xd009642/tarpaulin#13) the folders it goes into were just a first idea, so any better ones are welcome. Right now it creates a directory structure like: ``` given_path/ |_____ <filename>_rs_<linenum>/ |_____ ... |_____ <filename>_rs_<linenum>/ |_____ rust_out ``` I couldn't figure out where it actually outputs the file w/ the name, I suspect its somewhere deeper in the compiler. It also adds the unstable `--persist-doctests` flag to `rustdoc` that enables this behavior.
2 parents 527b8d4 + f5413cd commit f613dc1

File tree

6 files changed

+73
-8
lines changed

6 files changed

+73
-8
lines changed

Diff for: src/doc/rustdoc/src/unstable-features.md

+12
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,15 @@ JavaScript, and font files in a single location, rather than duplicating it once
417417
(grouping of crate docs generated into the same output directory, like with `cargo doc`). Per-crate
418418
files like the search index will still load from the documentation root, but anything that gets
419419
renamed with `--resource-suffix` will load from the given path.
420+
421+
### `--persist-doctests`: persist doctest executables after running
422+
423+
Using this flag looks like this:
424+
425+
```bash
426+
$ rustdoc src/lib.rs --test -Z unstable-options --persist-doctests target/rustdoctest
427+
```
428+
429+
This flag allows you to keep doctest executables around after they're compiled or run.
430+
Usually, rustdoc will immediately discard a compiled doctest after it's been tested, but
431+
with this option, you can keep those binaries around for farther testing.

Diff for: src/librustdoc/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ pub struct Options {
6868
pub should_test: bool,
6969
/// List of arguments to pass to the test harness, if running tests.
7070
pub test_args: Vec<String>,
71+
/// Optional path to persist the doctest executables to, defaults to a
72+
/// temporary directory if not set.
73+
pub persist_doctests: Option<PathBuf>,
7174

7275
// Options that affect the documentation process
7376

@@ -121,6 +124,7 @@ impl fmt::Debug for Options {
121124
.field("lint_cap", &self.lint_cap)
122125
.field("should_test", &self.should_test)
123126
.field("test_args", &self.test_args)
127+
.field("persist_doctests", &self.persist_doctests)
124128
.field("default_passes", &self.default_passes)
125129
.field("manual_passes", &self.manual_passes)
126130
.field("display_warnings", &self.display_warnings)
@@ -431,6 +435,7 @@ impl Options {
431435
let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some();
432436
let static_root_path = matches.opt_str("static-root-path");
433437
let generate_search_filter = !matches.opt_present("disable-per-crate-search");
438+
let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from);
434439

435440
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
436441

@@ -456,6 +461,7 @@ impl Options {
456461
manual_passes,
457462
display_warnings,
458463
crate_version,
464+
persist_doctests,
459465
render_options: RenderOptions {
460466
output,
461467
external_html,

Diff for: src/librustdoc/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,12 @@ fn opts() -> Vec<RustcOptGroup> {
341341
"disable-per-crate-search",
342342
"disables generating the crate selector on the search box")
343343
}),
344+
unstable("persist-doctests", |o| {
345+
o.optopt("",
346+
"persist-doctests",
347+
"Directory to persist doctest executables into",
348+
"PATH")
349+
}),
344350
]
345351
}
346352

Diff for: src/librustdoc/markdown.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub fn test(mut options: Options, diag: &errors::Handler) -> isize {
142142
options.libs, options.codegen_options, options.externs,
143143
true, opts, options.maybe_sysroot, None,
144144
Some(options.input),
145-
options.linker, options.edition);
145+
options.linker, options.edition, options.persist_doctests);
146146
collector.set_position(DUMMY_SP);
147147
let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
148148
let res = find_testable_code(&input_str, &mut collector, codes);

Diff for: src/librustdoc/test.rs

+46-5
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ pub fn run(mut options: Options) -> isize {
120120
Some(source_map),
121121
None,
122122
options.linker,
123-
options.edition
123+
options.edition,
124+
options.persist_doctests,
124125
);
125126

126127
{
@@ -184,7 +185,8 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
184185
cg: CodegenOptions, externs: Externs,
185186
should_panic: bool, no_run: bool, as_test_harness: bool,
186187
compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions,
187-
maybe_sysroot: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition) {
188+
maybe_sysroot: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition,
189+
persist_doctests: Option<PathBuf>) {
188190
// The test harness wants its own `main` and top-level functions, so
189191
// never wrap the test in `fn main() { ... }`.
190192
let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts);
@@ -249,6 +251,20 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
249251
let old = io::set_panic(Some(box Sink(data.clone())));
250252
let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout()));
251253

254+
enum DirState {
255+
Temp(tempfile::TempDir),
256+
Perm(PathBuf),
257+
}
258+
259+
impl DirState {
260+
fn path(&self) -> &std::path::Path {
261+
match self {
262+
DirState::Temp(t) => t.path(),
263+
DirState::Perm(p) => p.as_path(),
264+
}
265+
}
266+
}
267+
252268
let (libdir, outdir, compile_result) = driver::spawn_thread_pool(sessopts, |sessopts| {
253269
let source_map = Lrc::new(SourceMap::new(sessopts.file_path_mapping()));
254270
let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
@@ -267,7 +283,26 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
267283
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
268284

269285
let outdir = Mutex::new(
270-
TempFileBuilder::new().prefix("rustdoctest").tempdir().expect("rustdoc needs a tempdir")
286+
if let Some(mut path) = persist_doctests {
287+
path.push(format!("{}_{}",
288+
filename
289+
.to_string()
290+
.rsplit('/')
291+
.next()
292+
.unwrap()
293+
.replace(".", "_"),
294+
line)
295+
);
296+
std::fs::create_dir_all(&path)
297+
.expect("Couldn't create directory for doctest executables");
298+
299+
DirState::Perm(path)
300+
} else {
301+
DirState::Temp(TempFileBuilder::new()
302+
.prefix("rustdoctest")
303+
.tempdir()
304+
.expect("rustdoc needs a tempdir"))
305+
}
271306
);
272307
let libdir = sess.target_filesearch(PathKind::All).get_lib_path();
273308
let mut control = driver::CompileController::basic();
@@ -629,13 +664,15 @@ pub struct Collector {
629664
filename: Option<PathBuf>,
630665
linker: Option<PathBuf>,
631666
edition: Edition,
667+
persist_doctests: Option<PathBuf>,
632668
}
633669

634670
impl Collector {
635671
pub fn new(cratename: String, cfgs: Vec<String>, libs: Vec<SearchPath>, cg: CodegenOptions,
636672
externs: Externs, use_headers: bool, opts: TestOptions,
637673
maybe_sysroot: Option<PathBuf>, source_map: Option<Lrc<SourceMap>>,
638-
filename: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition) -> Collector {
674+
filename: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition,
675+
persist_doctests: Option<PathBuf>) -> Collector {
639676
Collector {
640677
tests: Vec::new(),
641678
names: Vec::new(),
@@ -652,6 +689,7 @@ impl Collector {
652689
filename,
653690
linker,
654691
edition,
692+
persist_doctests,
655693
}
656694
}
657695

@@ -695,6 +733,8 @@ impl Tester for Collector {
695733
let maybe_sysroot = self.maybe_sysroot.clone();
696734
let linker = self.linker.clone();
697735
let edition = config.edition.unwrap_or(self.edition);
736+
let persist_doctests = self.persist_doctests.clone();
737+
698738
debug!("Creating test {}: {}", name, test);
699739
self.tests.push(testing::TestDescAndFn {
700740
desc: testing::TestDesc {
@@ -727,7 +767,8 @@ impl Tester for Collector {
727767
&opts,
728768
maybe_sysroot,
729769
linker,
730-
edition)
770+
edition,
771+
persist_doctests)
731772
}))
732773
} {
733774
Ok(()) => (),

Diff for: src/test/rustdoc-ui/failed-doctest-output.stdout

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ error[E0425]: cannot find value `no` in this scope
1212
3 | no
1313
| ^^ not found in this scope
1414

15-
thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:319:13
15+
thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:354:13
1616
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
1717

1818
---- $DIR/failed-doctest-output.rs - SomeStruct (line 11) stdout ----
@@ -21,7 +21,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 11)' panicked at 'test
2121
thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1
2222
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
2323

24-
', src/librustdoc/test.rs:354:17
24+
', src/librustdoc/test.rs:389:17
2525

2626

2727
failures:

0 commit comments

Comments
 (0)