Skip to content

Commit 1d719a4

Browse files
Move code to compile merged doctest and its caller into its own function
1 parent 0bffe4f commit 1d719a4

File tree

1 file changed

+89
-65
lines changed

1 file changed

+89
-65
lines changed

src/librustdoc/doctest.rs

Lines changed: 89 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,83 @@ impl RunnableDocTest {
560560
}
561561
}
562562

563+
fn compile_merged_doctest_and_caller_binary(
564+
mut child: process::Child,
565+
doctest: &RunnableDocTest,
566+
rustdoc_options: &RustdocOptions,
567+
rustc_binary: &Path,
568+
output_file: &Path,
569+
compiler_args: Vec<String>,
570+
test_code: &str,
571+
instant: Instant,
572+
) -> Result<process::Output, (Duration, Result<(), RustdocResult>)> {
573+
// compile-fail tests never get merged, so this should always pass
574+
let status = child.wait().expect("Failed to wait");
575+
576+
// the actual test runner is a separate component, built with nightly-only features;
577+
// build it now
578+
let runner_input_file = doctest.path_for_merged_doctest_runner();
579+
580+
let mut runner_compiler =
581+
wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
582+
// the test runner does not contain any user-written code, so this doesn't allow
583+
// the user to exploit nightly-only features on stable
584+
runner_compiler.env("RUSTC_BOOTSTRAP", "1");
585+
runner_compiler.args(compiler_args);
586+
runner_compiler.args(["--crate-type=bin", "-o"]).arg(output_file);
587+
let mut extern_path = std::ffi::OsString::from(format!(
588+
"--extern=doctest_bundle_{edition}=",
589+
edition = doctest.edition
590+
));
591+
592+
// Deduplicate passed -L directory paths, since usually all dependencies will be in the
593+
// same directory (e.g. target/debug/deps from Cargo).
594+
let mut seen_search_dirs = FxHashSet::default();
595+
for extern_str in &rustdoc_options.extern_strs {
596+
if let Some((_cratename, path)) = extern_str.split_once('=') {
597+
// Direct dependencies of the tests themselves are
598+
// indirect dependencies of the test runner.
599+
// They need to be in the library search path.
600+
let dir = Path::new(path)
601+
.parent()
602+
.filter(|x| x.components().count() > 0)
603+
.unwrap_or(Path::new("."));
604+
if seen_search_dirs.insert(dir) {
605+
runner_compiler.arg("-L").arg(dir);
606+
}
607+
}
608+
}
609+
let output_bundle_file = doctest
610+
.test_opts
611+
.outdir
612+
.path()
613+
.join(format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition));
614+
extern_path.push(&output_bundle_file);
615+
runner_compiler.arg(extern_path);
616+
runner_compiler.arg(&runner_input_file);
617+
if std::fs::write(&runner_input_file, test_code).is_err() {
618+
// If we cannot write this file for any reason, we leave. All combined tests will be
619+
// tested as standalone tests.
620+
return Err((instant.elapsed(), Err(RustdocResult::CompileError)));
621+
}
622+
if !rustdoc_options.nocapture {
623+
// If `nocapture` is disabled, then we don't display rustc's output when compiling
624+
// the merged doctests.
625+
runner_compiler.stderr(Stdio::null());
626+
}
627+
runner_compiler.arg("--error-format=short");
628+
debug!("compiler invocation for doctest runner: {runner_compiler:?}");
629+
630+
let status = if !status.success() {
631+
status
632+
} else {
633+
let mut child_runner = runner_compiler.spawn().expect("Failed to spawn rustc process");
634+
child_runner.wait().expect("Failed to wait")
635+
};
636+
637+
Ok(process::Output { status, stdout: Vec::new(), stderr: Vec::new() })
638+
}
639+
563640
/// Execute a `RunnableDoctest`.
564641
///
565642
/// This is the function that calculates the compiler command line, invokes the compiler, then
@@ -675,7 +752,6 @@ fn run_test(
675752
.arg(input_file);
676753
} else {
677754
compiler.arg("--crate-type=bin").arg("-o").arg(&output_file);
678-
// Setting these environment variables is unneeded if this is a merged doctest.
679755
compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
680756
compiler.env(
681757
"UNSTABLE_RUSTDOC_TEST_LINE",
@@ -690,71 +766,19 @@ fn run_test(
690766

691767
let mut child = compiler.spawn().expect("Failed to spawn rustc process");
692768
let output = if let Some(merged_test_code) = &doctest.merged_test_code {
693-
// compile-fail tests never get merged, so this should always pass
694-
let status = child.wait().expect("Failed to wait");
695-
696-
// the actual test runner is a separate component, built with nightly-only features;
697-
// build it now
698-
let runner_input_file = doctest.path_for_merged_doctest_runner();
699-
700-
let mut runner_compiler =
701-
wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
702-
// the test runner does not contain any user-written code, so this doesn't allow
703-
// the user to exploit nightly-only features on stable
704-
runner_compiler.env("RUSTC_BOOTSTRAP", "1");
705-
runner_compiler.args(compiler_args);
706-
runner_compiler.args(["--crate-type=bin", "-o"]).arg(&output_file);
707-
let mut extern_path = std::ffi::OsString::from(format!(
708-
"--extern=doctest_bundle_{edition}=",
709-
edition = doctest.edition
710-
));
711-
712-
// Deduplicate passed -L directory paths, since usually all dependencies will be in the
713-
// same directory (e.g. target/debug/deps from Cargo).
714-
let mut seen_search_dirs = FxHashSet::default();
715-
for extern_str in &rustdoc_options.extern_strs {
716-
if let Some((_cratename, path)) = extern_str.split_once('=') {
717-
// Direct dependencies of the tests themselves are
718-
// indirect dependencies of the test runner.
719-
// They need to be in the library search path.
720-
let dir = Path::new(path)
721-
.parent()
722-
.filter(|x| x.components().count() > 0)
723-
.unwrap_or(Path::new("."));
724-
if seen_search_dirs.insert(dir) {
725-
runner_compiler.arg("-L").arg(dir);
726-
}
727-
}
728-
}
729-
let output_bundle_file = doctest
730-
.test_opts
731-
.outdir
732-
.path()
733-
.join(format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition));
734-
extern_path.push(&output_bundle_file);
735-
runner_compiler.arg(extern_path);
736-
runner_compiler.arg(&runner_input_file);
737-
if std::fs::write(&runner_input_file, merged_test_code).is_err() {
738-
// If we cannot write this file for any reason, we leave. All combined tests will be
739-
// tested as standalone tests.
740-
return (instant.elapsed(), Err(RustdocResult::CompileError));
741-
}
742-
if !rustdoc_options.nocapture {
743-
// If `nocapture` is disabled, then we don't display rustc's output when compiling
744-
// the merged doctests.
745-
runner_compiler.stderr(Stdio::null());
769+
match compile_merged_doctest_and_caller_binary(
770+
child,
771+
&doctest,
772+
rustdoc_options,
773+
rustc_binary,
774+
&output_file,
775+
compiler_args,
776+
merged_test_code,
777+
instant,
778+
) {
779+
Ok(out) => out,
780+
Err(err) => return err,
746781
}
747-
runner_compiler.arg("--error-format=short");
748-
debug!("compiler invocation for doctest runner: {runner_compiler:?}");
749-
750-
let status = if !status.success() {
751-
status
752-
} else {
753-
let mut child_runner = runner_compiler.spawn().expect("Failed to spawn rustc process");
754-
child_runner.wait().expect("Failed to wait")
755-
};
756-
757-
process::Output { status, stdout: Vec::new(), stderr: Vec::new() }
758782
} else {
759783
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
760784
stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources");

0 commit comments

Comments
 (0)