33extern crate cargo_metadata;
44
55use std:: path:: { PathBuf , Path } ;
6- use std:: io:: Write ;
6+ use std:: io:: { self , Write } ;
77use std:: process:: Command ;
88
99
1010const CARGO_MIRI_HELP : & str = r#"Interprets bin crates
1111
1212Usage:
13- cargo miri [options] [--] [<opts>...]
13+ cargo miri [subcommand] [options] [--] [<opts>...]
14+
15+ Subcommands:
16+ run Run binaries (default)
17+ test Run tests
18+ setup Only perform automatic setup, but without asking questions (for getting a proper libstd)
1419
1520Common options:
1621 -h, --help Print this message
@@ -27,7 +32,7 @@ it to configure the resource limits
2732available resource limits are `memory_size`, `step_limit`, `stack_limit`
2833"# ;
2934
30- #[ derive( Copy , Clone , Debug ) ]
35+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
3136enum MiriCommand {
3237 Run ,
3338 Test ,
@@ -43,6 +48,11 @@ fn show_version() {
4348 env!( "CARGO_PKG_VERSION" ) , env!( "VERGEN_SHA_SHORT" ) , env!( "VERGEN_COMMIT_DATE" ) ) ;
4449}
4550
51+ fn show_error ( msg : String ) -> ! {
52+ eprintln ! ( "fatal error: {}" , msg) ;
53+ std:: process:: exit ( 1 )
54+ }
55+
4656fn list_targets ( mut args : impl Iterator < Item =String > ) -> impl Iterator < Item =cargo_metadata:: Target > {
4757 // We need to get the manifest, and then the metadata, to enumerate targets.
4858 let manifest_path_arg = args. find ( |val| {
@@ -91,6 +101,40 @@ fn list_targets(mut args: impl Iterator<Item=String>) -> impl Iterator<Item=carg
91101 package. targets . into_iter ( )
92102}
93103
104+ fn ask ( question : & str ) {
105+ let mut buf = String :: new ( ) ;
106+ print ! ( "{} [Y/n] " , question) ;
107+ io:: stdout ( ) . flush ( ) . unwrap ( ) ;
108+ io:: stdin ( ) . read_line ( & mut buf) . unwrap ( ) ;
109+ let answer = match buf. trim ( ) . to_lowercase ( ) . as_ref ( ) {
110+ "" | "y" | "yes" => true ,
111+ "n" | "no" => false ,
112+ a => show_error ( format ! ( "I do not understand `{}`" , a) )
113+ } ;
114+ if !answer {
115+ show_error ( format ! ( "Aborting as per your request" ) )
116+ }
117+ }
118+
119+ /// Perform the setup requires to make `cargo miri` work: Getting a custom-built libstd. Then sets MIRI_SYSROOT.
120+ /// Skipped if MIRI_SYSROOT is already set, in that case we expect the user has done all this already.
121+ fn setup ( ask_user : bool ) {
122+ if std:: env:: var ( "MIRI_SYSROOT" ) . is_ok ( ) {
123+ return ;
124+ }
125+
126+ // First, we need xargo
127+ if Command :: new ( "xargo" ) . arg ( "--version" ) . output ( ) . is_err ( )
128+ {
129+ if ask_user {
130+ ask ( "It seems you do not have xargo installed. I will run `cargo install xargo`. Proceed?" ) ;
131+ }
132+ if !Command :: new ( "cargo" ) . args ( & [ "install" , "xargo" ] ) . status ( ) . unwrap ( ) . success ( ) {
133+ show_error ( format ! ( "Failed to install xargo" ) ) ;
134+ }
135+ }
136+ }
137+
94138fn main ( ) {
95139 // Check for version and help flags even when invoked as 'cargo-miri'
96140 if std:: env:: args ( ) . any ( |a| a == "--help" || a == "-h" ) {
@@ -117,11 +161,15 @@ fn main() {
117161 None => ( MiriCommand :: Run , 2 ) ,
118162 // Unvalid command
119163 Some ( s) => {
120- eprintln ! ( "Unknown command `{}`" , s) ;
121- std:: process:: exit ( 1 )
164+ show_error ( format ! ( "Unknown command `{}`" , s) )
122165 }
123166 } ;
124167
168+ // We always setup
169+ let ask = subcommand != MiriCommand :: Setup ;
170+ setup ( ask) ;
171+
172+ // Now run the command.
125173 for target in list_targets ( std:: env:: args ( ) . skip ( skip) ) {
126174 let args = std:: env:: args ( ) . skip ( skip) ;
127175 let kind = target. kind . get ( 0 ) . expect (
0 commit comments