@@ -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 > ,
@@ -250,27 +250,56 @@ fn xargo_check() -> Command {
250
250
Command :: new ( env:: var_os ( "XARGO_CHECK" ) . unwrap_or_else ( || OsString :: from ( "xargo-check" ) ) )
251
251
}
252
252
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" ) ;
258
260
std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
259
261
}
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
+ }
260
271
}
261
272
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) ]
268
282
{
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)
271
292
}
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" ) ;
274
303
std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
275
304
}
276
305
}
@@ -890,6 +919,8 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
890
919
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
891
920
let env = CrateRunEnv :: collect ( args, inside_rustdoc) ;
892
921
922
+ store_json ( CrateRunInfo :: RunWith ( env. clone ( ) ) ) ;
923
+
893
924
// Rustdoc expects us to exit with an error code if the test is marked as `compile_fail`,
894
925
// just creating the JSON file is not enough: we need to detect syntax errors,
895
926
// 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) {
906
937
cmd. arg ( "--emit=metadata" ) ;
907
938
}
908
939
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) ;
910
949
cmd. env ( "MIRI_BE_RUSTC" , "target" ) ;
911
950
912
951
if verbose > 0 {
@@ -917,11 +956,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
917
956
eprintln ! ( "[cargo-miri rustc inside rustdoc] going to run:\n {:?}" , cmd) ;
918
957
}
919
958
920
- exec_with_pipe ( cmd, & env. stdin ) ;
959
+ exec_with_pipe ( cmd, & env. stdin , format ! ( "{}.stdin" , out_filename ( "" , "" ) . display ( ) ) ) ;
921
960
}
922
961
923
- store_json ( CrateRunInfo :: RunWith ( env) ) ;
924
-
925
962
return ;
926
963
}
927
964
@@ -997,8 +1034,6 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
997
1034
"[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} info_query={info_query}"
998
1035
) ;
999
1036
}
1000
- debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
1001
- exec ( cmd) ;
1002
1037
1003
1038
// Create a stub .rlib file if "link" was requested by cargo.
1004
1039
// 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) {
1013
1048
File :: create ( out_filename ( "" , ".dll" ) ) . expect ( "failed to create fake .dll file" ) ;
1014
1049
File :: create ( out_filename ( "" , ".lib" ) ) . expect ( "failed to create fake .lib file" ) ;
1015
1050
}
1051
+
1052
+ debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
1053
+ exec ( cmd) ;
1016
1054
}
1017
1055
1018
1056
#[ derive( Debug , Copy , Clone , PartialEq ) ]
@@ -1117,7 +1155,7 @@ fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: RunnerPhas
1117
1155
// Run it.
1118
1156
debug_cmd ( "[cargo-miri runner]" , verbose, & cmd) ;
1119
1157
match phase {
1120
- RunnerPhase :: Rustdoc => exec_with_pipe ( cmd, & info. stdin ) ,
1158
+ RunnerPhase :: Rustdoc => exec_with_pipe ( cmd, & info. stdin , format ! ( "{}.stdin" , binary ) ) ,
1121
1159
RunnerPhase :: Cargo => exec ( cmd) ,
1122
1160
}
1123
1161
}
0 commit comments