@@ -2,6 +2,7 @@ use std::env;
22use std:: ffi:: OsString ;
33use std:: fs:: { self , File } ;
44use std:: io:: { self , BufRead , BufReader , BufWriter , Write } ;
5+ use std:: iter:: TakeWhile ;
56use std:: ops:: Not ;
67use std:: path:: { Path , PathBuf } ;
78use std:: process:: Command ;
@@ -36,9 +37,9 @@ enum MiriCommand {
3637 Setup ,
3738}
3839
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 .
4041#[ derive( Serialize , Deserialize ) ]
41- struct CrateRunInfo {
42+ struct CrateRunEnv {
4243 /// The command-line arguments.
4344 args : Vec < String > ,
4445 /// The environment.
@@ -47,13 +48,22 @@ struct CrateRunInfo {
4748 current_dir : OsString ,
4849}
4950
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+
5060impl CrateRunInfo {
5161 /// Gather all the information we need.
5262 fn collect ( args : env:: Args ) -> Self {
5363 let args = args. collect ( ) ;
5464 let env = env:: vars_os ( ) . collect ( ) ;
5565 let current_dir = env:: current_dir ( ) . unwrap ( ) . into_os_string ( ) ;
56- CrateRunInfo { args, env, current_dir }
66+ Self :: RunWith ( CrateRunEnv { args, env, current_dir } )
5767 }
5868
5969 fn store ( & self , filename : & Path ) {
@@ -89,31 +99,50 @@ fn has_arg_flag(name: &str) -> bool {
8999 args. any ( |val| val == name)
90100}
91101
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,
103114 }
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+ }
113137 }
114138 }
115139}
116140
141+ /// Gets the value of a `--flag`.
142+ fn get_arg_flag_value ( name : & str ) -> Option < String > {
143+ ArgFlagValueIter :: new ( name) . next ( )
144+ }
145+
117146/// Returns the path to the `miri` binary
118147fn find_miri ( ) -> PathBuf {
119148 if let Some ( path) = env:: var_os ( "MIRI" ) {
@@ -436,14 +465,15 @@ fn phase_cargo_miri(mut args: env::Args) {
436465 // This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something,
437466 // and it later helps us detect which crates are proc-macro/build-script
438467 // (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 {
440471 target
441472 } else {
442473 // No target given. Pick default and tell cargo about it.
443- let host = version_info ( ) . host ;
444474 cmd. arg ( "--target" ) ;
445475 cmd. arg ( & host) ;
446- host
476+ & host
447477 } ;
448478
449479 // 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) {
495525 }
496526 cmd. env ( "RUSTC_WRAPPER" , & cargo_miri_path) ;
497527
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) ;
501538
502539 // Set rustdoc to us as well, so we can make it do nothing (see issue #584).
503540 cmd. env ( "RUSTDOC" , & cargo_miri_path) ;
504541
505542 // Run cargo.
506543 if verbose {
507544 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+ }
509549 eprintln ! ( "[cargo-miri miri] RUSTDOC={:?}" , cargo_miri_path) ;
510550 eprintln ! ( "[cargo-miri miri] {:?}" , cmd) ;
511551 cmd. env ( "MIRI_VERBOSE" , "" ) ; // This makes the other phases verbose.
@@ -568,23 +608,34 @@ fn phase_cargo_rustc(args: env::Args) {
568608 _ => { } ,
569609 }
570610
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 | {
578612 let filename = out_filename ( "" , "" ) ;
579613 if verbose {
580614 eprintln ! ( "[cargo-miri rustc] writing run info to `{}`" , filename. display( ) ) ;
581615 }
582-
583616 info. store ( & filename) ;
584617 // For Windows, do the same thing again with `.exe` appended to the filename.
585618 // (Need to do this here as cargo moves that "binary" to a different place before running it.)
586619 info. store ( & out_filename ( "" , ".exe" ) ) ;
620+ } ;
621+
622+ let runnable_crate = !print && is_runnable_crate ( ) ;
587623
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 ) ;
588639 return ;
589640 }
590641
@@ -652,8 +703,16 @@ fn phase_cargo_runner(binary: &Path, binary_args: env::Args) {
652703 let file = File :: open ( & binary)
653704 . unwrap_or_else ( |_| show_error ( format ! ( "file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`" , binary) ) ) ;
654705 let file = BufReader :: new ( file) ;
655- let info: CrateRunInfo = serde_json:: from_reader ( file)
706+
707+ let info = serde_json:: from_reader ( file)
656708 . 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+ } ;
657716
658717 let mut cmd = miri ( ) ;
659718
0 commit comments