Skip to content

Commit 92b85ef

Browse files
Move code to compile merged doctest and its caller into its own function
1 parent 4e7be1a commit 92b85ef

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
@@ -559,6 +559,83 @@ impl RunnableDocTest {
559559
}
560560
}
561561

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

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

0 commit comments

Comments
 (0)