@@ -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 > ,
@@ -251,26 +251,73 @@ fn xargo_check() -> Command {
251
251
252
252
/// Execute the command. If it fails, fail this process with the same exit code.
253
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 ( ) {
257
- std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
254
+ fn exec ( mut cmd : Command ) -> ! {
255
+ // On non-Unix imitate POSIX exec as closely as we can
256
+ #[ cfg( not( unix) ) ]
257
+ {
258
+ exec ( cmd) ;
259
+ // Our imitation of exec only returns upon success
260
+ std:: process:: exit ( 0 )
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" )
258
270
}
259
271
}
260
272
261
273
/// Execute the command and pipe `input` into its stdin.
262
274
/// If it fails, fail this process with the same exit code.
263
275
/// Otherwise, continue.
264
276
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" ) ;
277
+ #[ cfg( unix) ]
267
278
{
268
- let stdin = child. stdin . as_mut ( ) . expect ( "failed to open stdin" ) ;
269
- stdin. write_all ( input) . expect ( "failed to write out test source" ) ;
279
+ use std:: os:: unix:: io:: FromRawFd ;
280
+ use std:: process:: Stdio ;
281
+
282
+ let mut fds: [ libc:: c_int ; 2 ] = [ 0 , 0 ] ;
283
+ // SAFETY: fds is an array of 2 libc::c_int
284
+ let res = unsafe { libc:: pipe ( fds. as_mut_ptr ( ) ) } ;
285
+ assert_eq ! ( res, 0 , "failed to create pipe" ) ;
286
+
287
+ // We need to set close-on-exec, otherwise our pipe isn't readable after exec
288
+ // SAFETY: fcntl has no preconditions
289
+ unsafe {
290
+ for fd in & fds {
291
+ let res = libc:: fcntl (
292
+ * fd,
293
+ libc:: F_SETFD ,
294
+ libc:: fcntl ( * fd, libc:: F_GETFD ) | libc:: FD_CLOEXEC ,
295
+ ) ;
296
+ assert_eq ! ( res, 0 , "failed to set close-on-exec for pipe" ) ;
297
+ }
298
+ }
299
+
300
+ // SAFETY: Both elements of fds are open file descriptors, because pipe2 returned 0
301
+ let dst = unsafe { Stdio :: from_raw_fd ( fds[ 0 ] ) } ;
302
+ let mut src = unsafe { File :: from_raw_fd ( fds[ 1 ] ) } ;
303
+
304
+ src. write_all ( input) . expect ( "failed to write out test source" ) ;
305
+
306
+ cmd. stdin ( dst) ;
307
+ exec ( cmd)
270
308
}
271
- let exit_status = child. wait ( ) . expect ( "failed to run command" ) ;
272
- if exit_status. success ( ) . not ( ) {
273
- std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
309
+ #[ cfg( not( unix) ) ]
310
+ {
311
+ cmd. stdin ( process:: Stdio :: piped ( ) ) ;
312
+ let mut child = cmd. spawn ( ) . expect ( "failed to spawn process" ) ;
313
+ {
314
+ let stdin = child. stdin . as_mut ( ) . expect ( "failed to open stdin" ) ;
315
+ stdin. write_all ( input) . expect ( "failed to write out test source" ) ;
316
+ }
317
+ let exit_status = child. wait ( ) . expect ( "failed to run command" ) ;
318
+ if exit_status. success ( ) . not ( ) {
319
+ std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
320
+ }
274
321
}
275
322
}
276
323
@@ -872,6 +919,8 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
872
919
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
873
920
let env = CrateRunEnv :: collect ( args, inside_rustdoc) ;
874
921
922
+ store_json ( CrateRunInfo :: RunWith ( env. clone ( ) ) ) ;
923
+
875
924
// Rustdoc expects us to exit with an error code if the test is marked as `compile_fail`,
876
925
// just creating the JSON file is not enough: we need to detect syntax errors,
877
926
// so we need to run Miri with `MIRI_BE_RUSTC` for a check-only build.
@@ -888,7 +937,14 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
888
937
cmd. arg ( "--emit=metadata" ) ;
889
938
}
890
939
891
- cmd. args ( & env. args ) ;
940
+ let mut args = env. args . clone ( ) ;
941
+ for i in 0 ..args. len ( ) {
942
+ if args[ i] == "-o" {
943
+ args[ i + 1 ] = format ! ( "{}_miri" , args[ i + 1 ] ) ;
944
+ }
945
+ }
946
+
947
+ cmd. args ( & args) ;
892
948
cmd. env ( "MIRI_BE_RUSTC" , "target" ) ;
893
949
894
950
if verbose > 0 {
@@ -902,8 +958,6 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
902
958
exec_with_pipe ( cmd, & env. stdin ) ;
903
959
}
904
960
905
- store_json ( CrateRunInfo :: RunWith ( env) ) ;
906
-
907
961
return ;
908
962
}
909
963
@@ -983,8 +1037,6 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
983
1037
"[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} print={print}"
984
1038
) ;
985
1039
}
986
- debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
987
- exec ( cmd) ;
988
1040
989
1041
// Create a stub .rlib file if "link" was requested by cargo.
990
1042
// This is necessary to prevent cargo from doing rebuilds all the time.
@@ -999,6 +1051,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
999
1051
File :: create ( out_filename ( "" , ".dll" ) ) . expect ( "failed to create fake .dll file" ) ;
1000
1052
File :: create ( out_filename ( "" , ".lib" ) ) . expect ( "failed to create fake .lib file" ) ;
1001
1053
}
1054
+
1055
+ debug_cmd ( "[cargo-miri rustc]" , verbose, & cmd) ;
1056
+ exec ( cmd) ;
1002
1057
}
1003
1058
1004
1059
#[ derive( Debug , Copy , Clone , PartialEq ) ]
0 commit comments