@@ -51,7 +51,7 @@ enum MiriCommand {
51
51
}
52
52
53
53
/// The information to run a crate with the given environment.
54
- #[ derive( Serialize , Deserialize ) ]
54
+ #[ derive( Clone , Serialize , Deserialize ) ]
55
55
struct CrateRunEnv {
56
56
/// The command-line arguments.
57
57
args : Vec < String > ,
@@ -249,27 +249,57 @@ fn xargo_check() -> Command {
249
249
Command :: new ( env:: var_os ( "XARGO_CHECK" ) . unwrap_or_else ( || OsString :: from ( "xargo-check" ) ) )
250
250
}
251
251
252
- /// Execute the command. If it fails, fail this process with the same exit code.
253
- /// Otherwise, continue.
254
- fn exec ( mut cmd : Command ) {
255
- let exit_status = cmd. status ( ) . expect ( "failed to run command" ) ;
256
- if exit_status. success ( ) . not ( ) {
252
+ /// Execute the `Command`, where possible by replacing the current process with a new process
253
+ /// described by the `Command`. Then exit this process with the exit code of the new process.
254
+ fn exec ( mut cmd : Command ) -> ! {
255
+ // On non-Unix imitate POSIX exec as closely as we can
256
+ #[ cfg( not( unix) ) ]
257
+ {
258
+ let exit_status = cmd. status ( ) . expect ( "failed to run command" ) ;
257
259
std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
258
260
}
261
+ // On Unix targets, actually exec
262
+ // If exec returns, process setup has failed. This is the same error condition as the expect in
263
+ // the non-Unix case.
264
+ #[ cfg( unix) ]
265
+ {
266
+ use std:: os:: unix:: process:: CommandExt ;
267
+ let error = cmd. exec ( ) ;
268
+ Err ( error) . expect ( "failed to run command" )
269
+ }
259
270
}
260
271
261
- /// Execute the command and pipe `input` into its stdin.
262
- /// If it fails, fail this process with the same exit code.
263
- /// Otherwise, continue.
264
- fn exec_with_pipe ( mut cmd : Command , input : & [ u8 ] ) {
265
- cmd. stdin ( process:: Stdio :: piped ( ) ) ;
266
- let mut child = cmd. spawn ( ) . expect ( "failed to spawn process" ) ;
272
+ /// Execute the `Command`, where possible by replacing the current process with a new process
273
+ /// described by the `Command`. Then exit this process with the exit code of the new process.
274
+ /// `input` is also piped to the new process's stdin, on cfg(unix) platforms by writing its
275
+ /// contents to `path` first, then setting stdin to that file.
276
+ fn exec_with_pipe < P > ( mut cmd : Command , input : & [ u8 ] , path : P ) -> !
277
+ where
278
+ P : AsRef < Path > ,
279
+ {
280
+ #[ cfg( unix) ]
267
281
{
268
- let stdin = child. stdin . as_mut ( ) . expect ( "failed to open stdin" ) ;
269
- stdin. write_all ( input) . expect ( "failed to write out test source" ) ;
282
+ // Write the bytes we want to send to stdin out to a file
283
+ std:: fs:: write ( & path, input) . unwrap ( ) ;
284
+
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
+
289
+ // Unlink the file so that it is fully cleaned up as soon as the new process exits
290
+ std:: fs:: remove_file ( & path) . unwrap ( ) ;
291
+ exec ( cmd)
270
292
}
271
- let exit_status = child. wait ( ) . expect ( "failed to run command" ) ;
272
- 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" ) ;
273
303
std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
274
304
}
275
305
}
@@ -872,6 +902,8 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
872
902
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
873
903
let env = CrateRunEnv :: collect ( args, inside_rustdoc) ;
874
904
905
+ store_json ( CrateRunInfo :: RunWith ( env. clone ( ) ) ) ;
906
+
875
907
// Rustdoc expects us to exit with an error code if the test is marked as `compile_fail`,
876
908
// just creating the JSON file is not enough: we need to detect syntax errors,
877
909
// so we need to run Miri with `MIRI_BE_RUSTC` for a check-only build.
@@ -888,7 +920,15 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
888
920
cmd. arg ( "--emit=metadata" ) ;
889
921
}
890
922
891
- cmd. args ( & env. args ) ;
923
+ // Alter the `-o` parameter so that it does not overwrite the JSON file we stored above.
924
+ let mut args = env. args . clone ( ) ;
925
+ for i in 0 ..args. len ( ) {
926
+ if args[ i] == "-o" {
927
+ args[ i + 1 ] . push_str ( "_miri" ) ;
928
+ }
929
+ }
930
+
931
+ cmd. args ( & args) ;
892
932
cmd. env ( "MIRI_BE_RUSTC" , "target" ) ;
893
933
894
934
if verbose > 0 {
@@ -899,11 +939,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
899
939
eprintln ! ( "[cargo-miri rustc inside rustdoc] going to run:\n {:?}" , cmd) ;
900
940
}
901
941
902
- exec_with_pipe ( cmd, & env. stdin ) ;
942
+ exec_with_pipe ( cmd, & env. stdin , format ! ( "{}.stdin" , out_filename ( "" , "" ) . display ( ) ) ) ;
903
943
}
904
944
905
- store_json ( CrateRunInfo :: RunWith ( env) ) ;
906
-
907
945
return ;
908
946
}
909
947
@@ -983,8 +1021,6 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
983
1021
"[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} print={print}"
984
1022
) ;
985
1023
}
986
- debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
987
- exec ( cmd) ;
988
1024
989
1025
// Create a stub .rlib file if "link" was requested by cargo.
990
1026
// This is necessary to prevent cargo from doing rebuilds all the time.
@@ -999,6 +1035,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
999
1035
File :: create ( out_filename ( "" , ".dll" ) ) . expect ( "failed to create fake .dll file" ) ;
1000
1036
File :: create ( out_filename ( "" , ".lib" ) ) . expect ( "failed to create fake .lib file" ) ;
1001
1037
}
1038
+
1039
+ debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
1040
+ exec ( cmd) ;
1002
1041
}
1003
1042
1004
1043
#[ derive( Debug , Copy , Clone , PartialEq ) ]
@@ -1100,7 +1139,7 @@ fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: RunnerPhas
1100
1139
// Run it.
1101
1140
debug_cmd ( "[cargo-miri runner]" , verbose, & cmd) ;
1102
1141
match phase {
1103
- RunnerPhase :: Rustdoc => exec_with_pipe ( cmd, & info. stdin ) ,
1142
+ RunnerPhase :: Rustdoc => exec_with_pipe ( cmd, & info. stdin , format ! ( "{}.stdin" , binary ) ) ,
1104
1143
RunnerPhase :: Cargo => exec ( cmd) ,
1105
1144
}
1106
1145
}
0 commit comments