@@ -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