@@ -51,7 +51,7 @@ enum MiriCommand {
5151}
5252
5353/// The information to run a crate with the given environment.
54- #[ derive( Serialize , Deserialize ) ]
54+ #[ derive( Clone , Serialize , Deserialize ) ]
5555struct CrateRunEnv {
5656 /// The command-line arguments.
5757 args : Vec < String > ,
@@ -250,27 +250,56 @@ fn xargo_check() -> Command {
250250 Command :: new ( env:: var_os ( "XARGO_CHECK" ) . unwrap_or_else ( || OsString :: from ( "xargo-check" ) ) )
251251}
252252
253- /// Execute the command. If it fails, fail this process with the same exit code.
254- /// Otherwise, continue.
255- fn exec ( mut cmd : Command ) {
256- let exit_status = cmd. status ( ) . expect ( "failed to run command" ) ;
257- if exit_status. success ( ) . not ( ) {
253+ /// Execute the `Command`, where possible by replacing the current process with a new process
254+ /// described by the `Command`. Then exit this process with the exit code of the new process.
255+ fn exec ( mut cmd : Command ) -> ! {
256+ // On non-Unix imitate POSIX exec as closely as we can
257+ #[ cfg( not( unix) ) ]
258+ {
259+ let exit_status = cmd. status ( ) . expect ( "failed to run command" ) ;
258260 std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
259261 }
262+ // On Unix targets, actually exec.
263+ // If exec returns, process setup has failed. This is the same error condition as the expect in
264+ // the non-Unix case.
265+ #[ cfg( unix) ]
266+ {
267+ use std:: os:: unix:: process:: CommandExt ;
268+ let error = cmd. exec ( ) ;
269+ Err ( error) . expect ( "failed to run command" )
270+ }
260271}
261272
262- /// Execute the command and pipe `input` into its stdin.
263- /// If it fails, fail this process with the same exit code.
264- /// Otherwise, continue.
265- fn exec_with_pipe ( mut cmd : Command , input : & [ u8 ] ) {
266- cmd. stdin ( process:: Stdio :: piped ( ) ) ;
267- let mut child = cmd. spawn ( ) . expect ( "failed to spawn process" ) ;
273+ /// Execute the `Command`, where possible by replacing the current process with a new process
274+ /// described by the `Command`. Then exit this process with the exit code of the new process.
275+ /// `input` is also piped to the new process's stdin, on cfg(unix) platforms by writing its
276+ /// contents to `path` first, then setting stdin to that file.
277+ fn exec_with_pipe < P > ( mut cmd : Command , input : & [ u8 ] , path : P ) -> !
278+ where
279+ P : AsRef < Path > ,
280+ {
281+ #[ cfg( unix) ]
268282 {
269- let stdin = child. stdin . as_mut ( ) . expect ( "failed to open stdin" ) ;
270- stdin. write_all ( input) . expect ( "failed to write out test source" ) ;
283+ // Write the bytes we want to send to stdin out to a file
284+ std:: fs:: write ( & path, input) . unwrap ( ) ;
285+ // Open the file for reading, and set our new stdin to it
286+ let stdin = File :: open ( & path) . unwrap ( ) ;
287+ cmd. stdin ( stdin) ;
288+ // Unlink the file so that it is fully cleaned up as soon as the new process exits
289+ std:: fs:: remove_file ( & path) . unwrap ( ) ;
290+ // Finally, we can hand off control.
291+ exec ( cmd)
271292 }
272- let exit_status = child. wait ( ) . expect ( "failed to run command" ) ;
273- if exit_status. success ( ) . not ( ) {
293+ #[ cfg( not( unix) ) ]
294+ {
295+ drop ( path) ; // We don't need the path, we can pipe the bytes directly
296+ cmd. stdin ( process:: Stdio :: piped ( ) ) ;
297+ let mut child = cmd. spawn ( ) . expect ( "failed to spawn process" ) ;
298+ {
299+ let stdin = child. stdin . as_mut ( ) . expect ( "failed to open stdin" ) ;
300+ stdin. write_all ( input) . expect ( "failed to write out test source" ) ;
301+ }
302+ let exit_status = child. wait ( ) . expect ( "failed to run command" ) ;
274303 std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
275304 }
276305}
@@ -890,6 +919,8 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
890919 // and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
891920 let env = CrateRunEnv :: collect ( args, inside_rustdoc) ;
892921
922+ store_json ( CrateRunInfo :: RunWith ( env. clone ( ) ) ) ;
923+
893924 // Rustdoc expects us to exit with an error code if the test is marked as `compile_fail`,
894925 // just creating the JSON file is not enough: we need to detect syntax errors,
895926 // so we need to run Miri with `MIRI_BE_RUSTC` for a check-only build.
@@ -906,7 +937,15 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
906937 cmd. arg ( "--emit=metadata" ) ;
907938 }
908939
909- cmd. args ( & env. args ) ;
940+ // Alter the `-o` parameter so that it does not overwrite the JSON file we stored above.
941+ let mut args = env. args . clone ( ) ;
942+ for i in 0 ..args. len ( ) {
943+ if args[ i] == "-o" {
944+ args[ i + 1 ] . push_str ( ".miri" ) ;
945+ }
946+ }
947+
948+ cmd. args ( & args) ;
910949 cmd. env ( "MIRI_BE_RUSTC" , "target" ) ;
911950
912951 if verbose > 0 {
@@ -917,11 +956,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
917956 eprintln ! ( "[cargo-miri rustc inside rustdoc] going to run:\n {:?}" , cmd) ;
918957 }
919958
920- exec_with_pipe ( cmd, & env. stdin ) ;
959+ exec_with_pipe ( cmd, & env. stdin , format ! ( "{}.stdin" , out_filename ( "" , "" ) . display ( ) ) ) ;
921960 }
922961
923- store_json ( CrateRunInfo :: RunWith ( env) ) ;
924-
925962 return ;
926963 }
927964
@@ -997,8 +1034,6 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
9971034 "[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} info_query={info_query}"
9981035 ) ;
9991036 }
1000- debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
1001- exec ( cmd) ;
10021037
10031038 // Create a stub .rlib file if "link" was requested by cargo.
10041039 // This is necessary to prevent cargo from doing rebuilds all the time.
@@ -1013,6 +1048,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
10131048 File :: create ( out_filename ( "" , ".dll" ) ) . expect ( "failed to create fake .dll file" ) ;
10141049 File :: create ( out_filename ( "" , ".lib" ) ) . expect ( "failed to create fake .lib file" ) ;
10151050 }
1051+
1052+ debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
1053+ exec ( cmd) ;
10161054}
10171055
10181056#[ derive( Debug , Copy , Clone , PartialEq ) ]
@@ -1117,7 +1155,7 @@ fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: RunnerPhas
11171155 // Run it.
11181156 debug_cmd ( "[cargo-miri runner]" , verbose, & cmd) ;
11191157 match phase {
1120- RunnerPhase :: Rustdoc => exec_with_pipe ( cmd, & info. stdin ) ,
1158+ RunnerPhase :: Rustdoc => exec_with_pipe ( cmd, & info. stdin , format ! ( "{}.stdin" , binary ) ) ,
11211159 RunnerPhase :: Cargo => exec ( cmd) ,
11221160 }
11231161}
0 commit comments