Skip to content

Commit 391ff8c

Browse files
Move code to compile merged doctest and its caller into its own function
1 parent f36b0ba commit 391ff8c

File tree

1 file changed

+97
-72
lines changed

1 file changed

+97
-72
lines changed

src/librustdoc/doctest.rs

Lines changed: 97 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,90 @@ 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+
610+
let output_bundle_file = doctest
611+
.test_opts
612+
.outdir
613+
.path()
614+
.join(format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition));
615+
extern_path.push(&output_bundle_file);
616+
runner_compiler.arg(extern_path);
617+
runner_compiler.arg(&runner_input_file);
618+
if std::fs::write(&runner_input_file, test_code).is_err() {
619+
// If we cannot write this file for any reason, we leave. All combined tests will be
620+
// tested as standalone tests.
621+
return Err((instant.elapsed(), Err(RustdocResult::CompileError)));
622+
}
623+
if !rustdoc_options.no_capture {
624+
// If `no_capture` is disabled, then we don't display rustc's output when compiling
625+
// the merged doctests.
626+
runner_compiler.stderr(Stdio::null());
627+
}
628+
runner_compiler.arg("--error-format=short");
629+
debug!("compiler invocation for doctest runner: {runner_compiler:?}");
630+
631+
let status = if !status.success() {
632+
status
633+
} else {
634+
let mut child_runner = match runner_compiler.spawn() {
635+
Ok(child) => child,
636+
Err(error) => {
637+
eprintln!("Failed to spawn {:?}: {error:?}", runner_compiler.get_program());
638+
return Err((Duration::default(), Err(RustdocResult::CompileError)));
639+
}
640+
};
641+
child_runner.wait().expect("Failed to wait")
642+
};
643+
644+
Ok(process::Output { status, stdout: Vec::new(), stderr: Vec::new() })
645+
}
646+
563647
/// Execute a `RunnableDoctest`.
564648
///
565649
/// This is the function that calculates the compiler command line, invokes the compiler, then
@@ -675,7 +759,6 @@ fn run_test(
675759
.arg(input_file);
676760
} else {
677761
compiler.arg("--crate-type=bin").arg("-o").arg(&output_file);
678-
// Setting these environment variables is unneeded if this is a merged doctest.
679762
compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
680763
compiler.env(
681764
"UNSTABLE_RUSTDOC_TEST_LINE",
@@ -692,81 +775,23 @@ fn run_test(
692775
Ok(child) => child,
693776
Err(error) => {
694777
eprintln!("Failed to spawn {:?}: {error:?}", compiler.get_program());
695-
return (Duration::default(), Err(TestFailure::CompileError));
778+
return (Duration::default(), Err(RustdocResult::CompileError));
696779
}
697780
};
698781
let output = if let Some(merged_test_code) = &doctest.merged_test_code {
699-
// compile-fail tests never get merged, so this should always pass
700-
let status = child.wait().expect("Failed to wait");
701-
702-
// the actual test runner is a separate component, built with nightly-only features;
703-
// build it now
704-
let runner_input_file = doctest.path_for_merged_doctest_runner();
705-
706-
let mut runner_compiler =
707-
wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
708-
// the test runner does not contain any user-written code, so this doesn't allow
709-
// the user to exploit nightly-only features on stable
710-
runner_compiler.env("RUSTC_BOOTSTRAP", "1");
711-
runner_compiler.args(compiler_args);
712-
runner_compiler.args(["--crate-type=bin", "-o"]).arg(&output_file);
713-
let mut extern_path = std::ffi::OsString::from(format!(
714-
"--extern=doctest_bundle_{edition}=",
715-
edition = doctest.edition
716-
));
717-
718-
// Deduplicate passed -L directory paths, since usually all dependencies will be in the
719-
// same directory (e.g. target/debug/deps from Cargo).
720-
let mut seen_search_dirs = FxHashSet::default();
721-
for extern_str in &rustdoc_options.extern_strs {
722-
if let Some((_cratename, path)) = extern_str.split_once('=') {
723-
// Direct dependencies of the tests themselves are
724-
// indirect dependencies of the test runner.
725-
// They need to be in the library search path.
726-
let dir = Path::new(path)
727-
.parent()
728-
.filter(|x| x.components().count() > 0)
729-
.unwrap_or(Path::new("."));
730-
if seen_search_dirs.insert(dir) {
731-
runner_compiler.arg("-L").arg(dir);
732-
}
733-
}
734-
}
735-
let output_bundle_file = doctest
736-
.test_opts
737-
.outdir
738-
.path()
739-
.join(format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition));
740-
extern_path.push(&output_bundle_file);
741-
runner_compiler.arg(extern_path);
742-
runner_compiler.arg(&runner_input_file);
743-
if std::fs::write(&runner_input_file, merged_test_code).is_err() {
744-
// If we cannot write this file for any reason, we leave. All combined tests will be
745-
// tested as standalone tests.
746-
return (instant.elapsed(), Err(RustdocResult::CompileError));
747-
}
748-
if !rustdoc_options.no_capture {
749-
// If `no_capture` is disabled, then we don't display rustc's output when compiling
750-
// the merged doctests.
751-
runner_compiler.stderr(Stdio::null());
782+
match compile_merged_doctest_and_caller_binary(
783+
child,
784+
&doctest,
785+
rustdoc_options,
786+
rustc_binary,
787+
&output_file,
788+
compiler_args,
789+
merged_test_code,
790+
instant,
791+
) {
792+
Ok(out) => out,
793+
Err(err) => return err,
752794
}
753-
runner_compiler.arg("--error-format=short");
754-
debug!("compiler invocation for doctest runner: {runner_compiler:?}");
755-
756-
let status = if !status.success() {
757-
status
758-
} else {
759-
let mut child_runner = match runner_compiler.spawn() {
760-
Ok(child) => child,
761-
Err(error) => {
762-
eprintln!("Failed to spawn {:?}: {error:?}", runner_compiler.get_program());
763-
return (Duration::default(), Err(TestFailure::CompileError));
764-
}
765-
};
766-
child_runner.wait().expect("Failed to wait")
767-
};
768-
769-
process::Output { status, stdout: Vec::new(), stderr: Vec::new() }
770795
} else {
771796
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
772797
stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources");

0 commit comments

Comments
 (0)