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