@@ -2,6 +2,7 @@ use std::env;
2
2
use std:: ffi:: OsString ;
3
3
use std:: fs:: { self , File } ;
4
4
use std:: io:: { self , BufRead , BufReader , BufWriter , Write } ;
5
+ use std:: iter:: TakeWhile ;
5
6
use std:: ops:: Not ;
6
7
use std:: path:: { Path , PathBuf } ;
7
8
use std:: process:: Command ;
@@ -36,9 +37,9 @@ enum MiriCommand {
36
37
Setup ,
37
38
}
38
39
39
- /// The inforamtion Miri needs to run a crate. Stored as JSON when the crate is "compiled" .
40
+ /// The information to run a crate with the given environment .
40
41
#[ derive( Serialize , Deserialize ) ]
41
- struct CrateRunInfo {
42
+ struct CrateRunEnv {
42
43
/// The command-line arguments.
43
44
args : Vec < String > ,
44
45
/// The environment.
@@ -47,13 +48,22 @@ struct CrateRunInfo {
47
48
current_dir : OsString ,
48
49
}
49
50
51
+ /// The information Miri needs to run a crate. Stored as JSON when the crate is "compiled".
52
+ #[ derive( Serialize , Deserialize ) ]
53
+ enum CrateRunInfo {
54
+ /// Run it with the given environment.
55
+ RunWith ( CrateRunEnv ) ,
56
+ /// Skip it as Miri does not support interpreting such kind of crates.
57
+ SkipProcMacroTest ,
58
+ }
59
+
50
60
impl CrateRunInfo {
51
61
/// Gather all the information we need.
52
62
fn collect ( args : env:: Args ) -> Self {
53
63
let args = args. collect ( ) ;
54
64
let env = env:: vars_os ( ) . collect ( ) ;
55
65
let current_dir = env:: current_dir ( ) . unwrap ( ) . into_os_string ( ) ;
56
- CrateRunInfo { args, env, current_dir }
66
+ Self :: RunWith ( CrateRunEnv { args, env, current_dir } )
57
67
}
58
68
59
69
fn store ( & self , filename : & Path ) {
@@ -89,31 +99,50 @@ fn has_arg_flag(name: &str) -> bool {
89
99
args. any ( |val| val == name)
90
100
}
91
101
92
- /// Gets the value of a `--flag`.
93
- fn get_arg_flag_value ( name : & str ) -> Option < String > {
94
- // Stop searching at `--`.
95
- let mut args = std:: env:: args ( ) . take_while ( |val| val != "--" ) ;
96
- loop {
97
- let arg = match args. next ( ) {
98
- Some ( arg) => arg,
99
- None => return None ,
100
- } ;
101
- if !arg. starts_with ( name) {
102
- continue ;
102
+ /// Yields all values of command line flag `name`.
103
+ struct ArgFlagValueIter < ' a > {
104
+ args : TakeWhile < env:: Args , fn ( & String ) -> bool > ,
105
+ name : & ' a str ,
106
+ }
107
+
108
+ impl < ' a > ArgFlagValueIter < ' a > {
109
+ fn new ( name : & ' a str ) -> Self {
110
+ Self {
111
+ // Stop searching at `--`.
112
+ args : env:: args ( ) . take_while ( |val| val != "--" ) ,
113
+ name,
103
114
}
104
- // Strip leading `name`.
105
- let suffix = & arg[ name. len ( ) ..] ;
106
- if suffix. is_empty ( ) {
107
- // This argument is exactly `name`; the next one is the value.
108
- return args. next ( ) ;
109
- } else if suffix. starts_with ( '=' ) {
110
- // This argument is `name=value`; get the value.
111
- // Strip leading `=`.
112
- return Some ( suffix[ 1 ..] . to_owned ( ) ) ;
115
+ }
116
+ }
117
+
118
+ impl Iterator for ArgFlagValueIter < ' _ > {
119
+ type Item = String ;
120
+
121
+ fn next ( & mut self ) -> Option < Self :: Item > {
122
+ loop {
123
+ let arg = self . args . next ( ) ?;
124
+ if !arg. starts_with ( self . name ) {
125
+ continue ;
126
+ }
127
+ // Strip leading `name`.
128
+ let suffix = & arg[ self . name . len ( ) ..] ;
129
+ if suffix. is_empty ( ) {
130
+ // This argument is exactly `name`; the next one is the value.
131
+ return self . args . next ( ) ;
132
+ } else if suffix. starts_with ( '=' ) {
133
+ // This argument is `name=value`; get the value.
134
+ // Strip leading `=`.
135
+ return Some ( suffix[ 1 ..] . to_owned ( ) ) ;
136
+ }
113
137
}
114
138
}
115
139
}
116
140
141
+ /// Gets the value of a `--flag`.
142
+ fn get_arg_flag_value ( name : & str ) -> Option < String > {
143
+ ArgFlagValueIter :: new ( name) . next ( )
144
+ }
145
+
117
146
/// Returns the path to the `miri` binary
118
147
fn find_miri ( ) -> PathBuf {
119
148
if let Some ( path) = env:: var_os ( "MIRI" ) {
@@ -436,14 +465,15 @@ fn phase_cargo_miri(mut args: env::Args) {
436
465
// This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something,
437
466
// and it later helps us detect which crates are proc-macro/build-script
438
467
// (host crates) and which crates are needed for the program itself.
439
- let target = if let Some ( target) = get_arg_flag_value ( "--target" ) {
468
+ let host = version_info ( ) . host ;
469
+ let target = get_arg_flag_value ( "--target" ) ;
470
+ let target = if let Some ( ref target) = target {
440
471
target
441
472
} else {
442
473
// No target given. Pick default and tell cargo about it.
443
- let host = version_info ( ) . host ;
444
474
cmd. arg ( "--target" ) ;
445
475
cmd. arg ( & host) ;
446
- host
476
+ & host
447
477
} ;
448
478
449
479
// Forward all further arguments. We do some processing here because we want to
@@ -495,17 +525,27 @@ fn phase_cargo_miri(mut args: env::Args) {
495
525
}
496
526
cmd. env ( "RUSTC_WRAPPER" , & cargo_miri_path) ;
497
527
498
- // Set the runner for the current target to us as well, so we can interpret the binaries.
499
- let runner_env_name = format ! ( "CARGO_TARGET_{}_RUNNER" , target. to_uppercase( ) . replace( '-' , "_" ) ) ;
500
- cmd. env ( & runner_env_name, & cargo_miri_path) ;
528
+ let runner_env_name = |triple : & str | {
529
+ format ! ( "CARGO_TARGET_{}_RUNNER" , triple. to_uppercase( ) . replace( '-' , "_" ) )
530
+ } ;
531
+ let host_runner_env_name = runner_env_name ( & host) ;
532
+ let target_runner_env_name = runner_env_name ( target) ;
533
+ // Set the target runner to us, so we can interpret the binaries.
534
+ cmd. env ( & target_runner_env_name, & cargo_miri_path) ;
535
+ // Unit tests of `proc-macro` crates are run on the host, so we set the host runner to
536
+ // us in order to skip them.
537
+ cmd. env ( & host_runner_env_name, & cargo_miri_path) ;
501
538
502
539
// Set rustdoc to us as well, so we can make it do nothing (see issue #584).
503
540
cmd. env ( "RUSTDOC" , & cargo_miri_path) ;
504
541
505
542
// Run cargo.
506
543
if verbose {
507
544
eprintln ! ( "[cargo-miri miri] RUSTC_WRAPPER={:?}" , cargo_miri_path) ;
508
- eprintln ! ( "[cargo-miri miri] {}={:?}" , runner_env_name, cargo_miri_path) ;
545
+ eprintln ! ( "[cargo-miri miri] {}={:?}" , target_runner_env_name, cargo_miri_path) ;
546
+ if * target != host {
547
+ eprintln ! ( "[cargo-miri miri] {}={:?}" , host_runner_env_name, cargo_miri_path) ;
548
+ }
509
549
eprintln ! ( "[cargo-miri miri] RUSTDOC={:?}" , cargo_miri_path) ;
510
550
eprintln ! ( "[cargo-miri miri] {:?}" , cmd) ;
511
551
cmd. env ( "MIRI_VERBOSE" , "" ) ; // This makes the other phases verbose.
@@ -568,23 +608,34 @@ fn phase_cargo_rustc(args: env::Args) {
568
608
_ => { } ,
569
609
}
570
610
571
- if !print && target_crate && is_runnable_crate ( ) {
572
- // This is the binary or test crate that we want to interpret under Miri.
573
- // But we cannot run it here, as cargo invoked us as a compiler -- our stdin and stdout are not
574
- // like we want them.
575
- // Instead of compiling, we write JSON into the output file with all the relevant command-line flags
576
- // and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
577
- let info = CrateRunInfo :: collect ( args) ;
611
+ let store_json = |info : CrateRunInfo | {
578
612
let filename = out_filename ( "" , "" ) ;
579
613
if verbose {
580
614
eprintln ! ( "[cargo-miri rustc] writing run info to `{}`" , filename. display( ) ) ;
581
615
}
582
-
583
616
info. store ( & filename) ;
584
617
// For Windows, do the same thing again with `.exe` appended to the filename.
585
618
// (Need to do this here as cargo moves that "binary" to a different place before running it.)
586
619
info. store ( & out_filename ( "" , ".exe" ) ) ;
620
+ } ;
621
+
622
+ let runnable_crate = !print && is_runnable_crate ( ) ;
587
623
624
+ if runnable_crate && target_crate {
625
+ // This is the binary or test crate that we want to interpret under Miri.
626
+ // But we cannot run it here, as cargo invoked us as a compiler -- our stdin and stdout are not
627
+ // like we want them.
628
+ // Instead of compiling, we write JSON into the output file with all the relevant command-line flags
629
+ // and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
630
+ store_json ( CrateRunInfo :: collect ( args) ) ;
631
+ return ;
632
+ }
633
+
634
+ if runnable_crate && ArgFlagValueIter :: new ( "--extern" ) . any ( |krate| krate == "proc_macro" ) {
635
+ // This is a "runnable" `proc-macro` crate (unit tests). We do not support
636
+ // interpreting that under Miri now, so we write a JSON file to (display a
637
+ // helpful message and) skip it in the runner phase.
638
+ store_json ( CrateRunInfo :: SkipProcMacroTest ) ;
588
639
return ;
589
640
}
590
641
@@ -652,8 +703,16 @@ fn phase_cargo_runner(binary: &Path, binary_args: env::Args) {
652
703
let file = File :: open ( & binary)
653
704
. unwrap_or_else ( |_| show_error ( format ! ( "file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`" , binary) ) ) ;
654
705
let file = BufReader :: new ( file) ;
655
- let info: CrateRunInfo = serde_json:: from_reader ( file)
706
+
707
+ let info = serde_json:: from_reader ( file)
656
708
. unwrap_or_else ( |_| show_error ( format ! ( "file {:?} contains outdated or invalid JSON; try `cargo clean`" , binary) ) ) ;
709
+ let info = match info {
710
+ CrateRunInfo :: RunWith ( info) => info,
711
+ CrateRunInfo :: SkipProcMacroTest => {
712
+ eprintln ! ( "Running unit tests of `proc-macro` crates is not currently supported by Miri." ) ;
713
+ return ;
714
+ }
715
+ } ;
657
716
658
717
let mut cmd = miri ( ) ;
659
718
0 commit comments