@@ -7,10 +7,10 @@ use std::io::{self, Write, BufRead};
7
7
use std:: process:: Command ;
8
8
use std:: fs:: { self , File } ;
9
9
10
- const CARGO_MIRI_HELP : & str = r#"Interprets bin crates
10
+ const CARGO_MIRI_HELP : & str = r#"Interprets bin crates and tests in Miri
11
11
12
12
Usage:
13
- cargo miri [subcommand] [options] [--] [<opts>...]
13
+ cargo miri [subcommand] [options] [--] [<miri opts>...]
14
14
15
15
Subcommands:
16
16
run Run binaries (default)
@@ -22,12 +22,13 @@ Common options:
22
22
--features Features to compile for the package
23
23
-V, --version Print version info and exit
24
24
25
- Other options are the same as `cargo rustc`.
25
+ Other [options] are the same as `cargo rustc`. Everything after the "--" is
26
+ passed verbatim to Miri.
26
27
27
- The feature `cargo- miri` is automatically defined for convenience. You can use
28
+ The config flag ` miri` is automatically defined for convenience. You can use
28
29
it to configure the resource limits
29
30
30
- #![cfg_attr(feature = "cargo- miri" , memory_size = 42)]
31
+ #![cfg_attr(miri, memory_size = 42)]
31
32
32
33
available resource limits are `memory_size`, `step_limit`, `stack_limit`
33
34
"# ;
@@ -53,23 +54,32 @@ fn show_error(msg: String) -> ! {
53
54
std:: process:: exit ( 1 )
54
55
}
55
56
57
+ // Determines whether a --flag is present
58
+ fn has_arg_flag ( name : & str ) -> bool {
59
+ let mut args = std:: env:: args ( ) . take_while ( |val| val != "--" ) ;
60
+ args. any ( |val| val == name)
61
+ }
62
+
63
+ /// Gets the value of a --flag
56
64
fn get_arg_flag_value ( name : & str ) -> Option < String > {
57
65
// stop searching at `--`
58
- let mut args = std:: env:: args ( ) . skip_while ( |val| !( val. starts_with ( name) || val == "--" ) ) ;
59
-
60
- match args. next ( ) {
61
- Some ( ref p) if p == "--" => None ,
62
- Some ( ref p) if p == name => args. next ( ) ,
63
- Some ( p) => {
64
- // Make sure this really starts with `$name=`, we didn't test for the `=` yet.
65
- let v = & p[ name. len ( ) ..] ; // strip leading `$name`
66
- if v. starts_with ( '=' ) {
67
- Some ( v[ 1 ..] . to_owned ( ) ) // strip leading `=`
68
- } else {
69
- None
70
- }
71
- } ,
72
- None => None ,
66
+ let mut args = std:: env:: args ( ) . take_while ( |val| val != "--" ) ;
67
+ loop {
68
+ let arg = match args. next ( ) {
69
+ Some ( arg) => arg,
70
+ None => return None ,
71
+ } ;
72
+ if !arg. starts_with ( name) {
73
+ continue ;
74
+ }
75
+ let suffix = & arg[ name. len ( ) ..] ; // strip leading `name`
76
+ if suffix. is_empty ( ) {
77
+ // This argument is exactly `name`, the next one is the value
78
+ return args. next ( ) ;
79
+ } else if suffix. starts_with ( '=' ) {
80
+ // This argument is `name=value`, get the value
81
+ return Some ( suffix[ 1 ..] . to_owned ( ) ) ; // strip leading `=`
82
+ }
73
83
}
74
84
}
75
85
@@ -272,167 +282,163 @@ fn main() {
272
282
// each applicable target, but with the RUSTC env var set to the `cargo-miri`
273
283
// binary so that we come back in the other branch, and dispatch
274
284
// the invocations to rustc and miri, respectively.
275
-
276
- let ( subcommand, skip) = match std:: env:: args ( ) . nth ( 2 ) . deref ( ) {
277
- Some ( "test" ) => ( MiriCommand :: Test , 3 ) ,
278
- Some ( "run" ) => ( MiriCommand :: Run , 3 ) ,
279
- Some ( "setup" ) => ( MiriCommand :: Setup , 3 ) ,
280
- // Default command, if there is an option or nothing
281
- Some ( s) if s. starts_with ( "-" ) => ( MiriCommand :: Run , 2 ) ,
282
- None => ( MiriCommand :: Run , 2 ) ,
283
- // Unvalid command
284
- Some ( s) => {
285
- show_error ( format ! ( "Unknown command `{}`" , s) )
286
- }
287
- } ;
288
-
289
- // We always setup
290
- let ask = subcommand != MiriCommand :: Setup ;
291
- setup ( ask) ;
292
- if subcommand == MiriCommand :: Setup {
293
- // Stop here.
294
- return ;
295
- }
296
-
297
- // Now run the command.
298
- for target in list_targets ( ) {
299
- let args = std:: env:: args ( ) . skip ( skip) ;
300
- let kind = target. kind . get ( 0 ) . expect (
301
- "badly formatted cargo metadata: target::kind is an empty array" ,
302
- ) ;
303
- match ( subcommand, & kind[ ..] ) {
304
- ( MiriCommand :: Test , "test" ) => {
305
- // For test binaries we call `cargo rustc --test target -- <rustc args>`
306
- if let Err ( code) = process (
307
- vec ! [ "--test" . to_string( ) , target. name] . into_iter ( ) . chain (
308
- args,
309
- ) ,
310
- )
311
- {
312
- std:: process:: exit ( code) ;
313
- }
314
- }
315
- ( MiriCommand :: Test , "lib" ) => {
316
- // For libraries we call `cargo rustc -- --test <rustc args>`
317
- // Notice now that `--test` is a rustc arg rather than a cargo arg. This tells
318
- // rustc to build a test harness which calls all #[test] functions.
319
- // We then execute that harness just like any other binary.
320
- if let Err ( code) = process (
321
- vec ! [ "--" . to_string( ) , "--test" . to_string( ) ] . into_iter ( ) . chain (
322
- args,
323
- ) ,
324
- )
325
- {
326
- std:: process:: exit ( code) ;
327
- }
328
- }
329
- ( MiriCommand :: Run , "bin" ) => {
330
- // For ordinary binaries we call `cargo rustc --bin target -- <rustc args>`
331
- if let Err ( code) = process (
332
- vec ! [ "--bin" . to_string( ) , target. name] . into_iter ( ) . chain (
333
- args,
334
- ) ,
335
- )
336
- {
337
- std:: process:: exit ( code) ;
338
- }
339
- }
340
- _ => { }
341
- }
342
- }
285
+ in_cargo_miri ( ) ;
343
286
} else if let Some ( "rustc" ) = std:: env:: args ( ) . nth ( 1 ) . as_ref ( ) . map ( AsRef :: as_ref) {
344
287
// This arm is executed when cargo-miri runs `cargo rustc` with the `RUSTC_WRAPPER` env var set to itself:
345
288
// Dependencies get dispatched to rustc, the final test/binary to miri.
289
+ inside_cargo_rustc ( ) ;
290
+ } else {
291
+ show_error ( format ! ( "Must be called with either `miri` or `rustc` as first argument." ) )
292
+ }
293
+ }
346
294
347
- let home = option_env ! ( "RUSTUP_HOME" ) . or ( option_env ! ( "MULTIRUST_HOME" ) ) ;
348
- let toolchain = option_env ! ( "RUSTUP_TOOLCHAIN" ) . or ( option_env ! ( "MULTIRUST_TOOLCHAIN" ) ) ;
349
- let sys_root = if let Ok ( sysroot) = :: std:: env:: var ( "MIRI_SYSROOT" ) {
350
- sysroot
351
- } else if let ( Some ( home) , Some ( toolchain) ) = ( home, toolchain) {
352
- format ! ( "{}/toolchains/{}" , home, toolchain)
353
- } else {
354
- option_env ! ( "RUST_SYSROOT" )
355
- . map ( |s| s. to_owned ( ) )
356
- . or_else ( || {
357
- Command :: new ( "rustc" )
358
- . arg ( "--print" )
359
- . arg ( "sysroot" )
360
- . output ( )
361
- . ok ( )
362
- . and_then ( |out| String :: from_utf8 ( out. stdout ) . ok ( ) )
363
- . map ( |s| s. trim ( ) . to_owned ( ) )
364
- } )
365
- . expect ( "need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust" )
366
- } ;
295
+ fn in_cargo_miri ( ) {
296
+ let ( subcommand, skip) = match std:: env:: args ( ) . nth ( 2 ) . deref ( ) {
297
+ Some ( "test" ) => ( MiriCommand :: Test , 3 ) ,
298
+ Some ( "run" ) => ( MiriCommand :: Run , 3 ) ,
299
+ Some ( "setup" ) => ( MiriCommand :: Setup , 3 ) ,
300
+ // Default command, if there is an option or nothing
301
+ Some ( s) if s. starts_with ( "-" ) => ( MiriCommand :: Run , 2 ) ,
302
+ None => ( MiriCommand :: Run , 2 ) ,
303
+ // Unvalid command
304
+ Some ( s) => {
305
+ show_error ( format ! ( "Unknown command `{}`" , s) )
306
+ }
307
+ } ;
308
+ let verbose = has_arg_flag ( "-v" ) ;
367
309
368
- // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
369
- // without having to pass --sysroot or anything
370
- let rustc_args = std:: env:: args ( ) . skip ( 2 ) ;
371
- let mut args: Vec < String > = if std:: env:: args ( ) . any ( |s| s == "--sysroot" ) {
372
- rustc_args. collect ( )
373
- } else {
374
- rustc_args
375
- . chain ( Some ( "--sysroot" . to_owned ( ) ) )
376
- . chain ( Some ( sys_root) )
377
- . collect ( )
378
- } ;
379
- args. splice ( 0 ..0 , miri:: miri_default_args ( ) . iter ( ) . map ( ToString :: to_string) ) ;
380
- args. extend_from_slice ( & [ "--cfg" . to_owned ( ) , r#"feature="cargo-miri""# . to_owned ( ) ] ) ;
381
-
382
- // this check ensures that dependencies are built but not interpreted and the final crate is
383
- // interpreted but not built
384
- let miri_enabled = std:: env:: args ( ) . any ( |s| s == "--emit=dep-info,metadata" ) ;
385
- let mut command = if miri_enabled {
386
- let mut path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
387
- path. set_file_name ( "miri" ) ;
388
- Command :: new ( path)
389
- } else {
390
- Command :: new ( "rustc" )
391
- } ;
392
- command. args ( & args) ;
310
+ // We always setup
311
+ let ask = subcommand != MiriCommand :: Setup ;
312
+ setup ( ask) ;
313
+ if subcommand == MiriCommand :: Setup {
314
+ // Stop here.
315
+ return ;
316
+ }
393
317
394
- match command. status ( ) {
395
- Ok ( exit) => {
396
- if !exit. success ( ) {
397
- std:: process:: exit ( exit. code ( ) . unwrap_or ( 42 ) ) ;
398
- }
318
+ // Now run the command.
319
+ for target in list_targets ( ) {
320
+ let mut args = std:: env:: args ( ) . skip ( skip) ;
321
+ let kind = target. kind . get ( 0 ) . expect (
322
+ "badly formatted cargo metadata: target::kind is an empty array" ,
323
+ ) ;
324
+ // Now we run `cargo rustc $FLAGS $ARGS`, giving the user the
325
+ // change to add additional flags. "FLAGS" is set to identify
326
+ // this target. The user gets to control what gets actually passed to Miri.
327
+ // However, we need to add a flag to what gets passed to rustc for the finaly
328
+ // binary, so that we know to interpret that with Miri.
329
+ // So after the first "--", we add "-Zcargo-miri-marker".
330
+ let mut cmd = Command :: new ( "cargo" ) ;
331
+ cmd. arg ( "rustc" ) ;
332
+ match ( subcommand, & kind[ ..] ) {
333
+ ( MiriCommand :: Run , "bin" ) => {
334
+ // FIXME: We just run all the binaries here.
335
+ // We should instead support `cargo miri --bin foo`.
336
+ cmd. arg ( "--bin" ) . arg ( target. name ) ;
337
+ }
338
+ ( MiriCommand :: Test , "test" ) => {
339
+ cmd. arg ( "--test" ) . arg ( target. name ) ;
399
340
}
400
- Err ( ref e) if miri_enabled => panic ! ( "error during miri run: {:?}" , e) ,
401
- Err ( ref e) => panic ! ( "error during rustc call: {:?}" , e) ,
341
+ ( MiriCommand :: Test , "lib" ) |
342
+ ( MiriCommand :: Test , "bin" ) => {
343
+ cmd. arg ( format ! ( "--{}" , kind) ) . arg ( target. name ) . arg ( "--profile" ) . arg ( "test" ) ;
344
+ }
345
+ // The remaining targets we do not even want to build
346
+ _ => continue ,
347
+ }
348
+ // add user-defined args until first "--"
349
+ while let Some ( arg) = args. next ( ) {
350
+ if arg == "--" {
351
+ break ;
352
+ }
353
+ cmd. arg ( arg) ;
354
+ }
355
+ // add "--" "-Zcargo-miri-marker" and the remaining user flags
356
+ cmd
357
+ . arg ( "--" )
358
+ . arg ( "cargo-miri-marker" )
359
+ . args ( args) ;
360
+ let path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
361
+ cmd. env ( "RUSTC_WRAPPER" , path) ;
362
+ if verbose {
363
+ eprintln ! ( "+ {:?}" , cmd) ;
364
+ }
365
+
366
+ let exit_status = cmd
367
+ . spawn ( )
368
+ . expect ( "could not run cargo" )
369
+ . wait ( )
370
+ . expect ( "failed to wait for cargo?" ) ;
371
+
372
+ if !exit_status. success ( ) {
373
+ std:: process:: exit ( exit_status. code ( ) . unwrap_or ( -1 ) )
402
374
}
403
- } else {
404
- show_error ( format ! ( "Must be called with either `miri` or `rustc` as first argument." ) )
405
375
}
406
376
}
407
377
408
- fn process < I > ( old_args : I ) -> Result < ( ) , i32 >
409
- where
410
- I : Iterator < Item = String > ,
411
- {
412
- let mut args = vec ! [ "rustc" . to_owned( ) ] ;
378
+ fn inside_cargo_rustc ( ) {
379
+ let home = option_env ! ( "RUSTUP_HOME" ) . or ( option_env ! ( "MULTIRUST_HOME" ) ) ;
380
+ let toolchain = option_env ! ( "RUSTUP_TOOLCHAIN" ) . or ( option_env ! ( "MULTIRUST_TOOLCHAIN" ) ) ;
381
+ let sys_root = if let Ok ( sysroot) = :: std:: env:: var ( "MIRI_SYSROOT" ) {
382
+ sysroot
383
+ } else if let ( Some ( home) , Some ( toolchain) ) = ( home, toolchain) {
384
+ format ! ( "{}/toolchains/{}" , home, toolchain)
385
+ } else {
386
+ option_env ! ( "RUST_SYSROOT" )
387
+ . map ( |s| s. to_owned ( ) )
388
+ . or_else ( || {
389
+ Command :: new ( "rustc" )
390
+ . arg ( "--print" )
391
+ . arg ( "sysroot" )
392
+ . output ( )
393
+ . ok ( )
394
+ . and_then ( |out| String :: from_utf8 ( out. stdout ) . ok ( ) )
395
+ . map ( |s| s. trim ( ) . to_owned ( ) )
396
+ } )
397
+ . expect ( "need to specify RUST_SYSROOT env var during miri compilation, or use rustup or multirust" )
398
+ } ;
413
399
414
- let mut found_dashes = false ;
415
- for arg in old_args {
416
- found_dashes |= arg == "--" ;
417
- args. push ( arg) ;
418
- }
419
- if !found_dashes {
420
- args. push ( "--" . to_owned ( ) ) ;
421
- }
422
- args. push ( "--emit=dep-info,metadata" . to_owned ( ) ) ;
423
-
424
- let path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
425
- let exit_status = Command :: new ( "cargo" )
426
- . args ( & args)
427
- . env ( "RUSTC_WRAPPER" , path)
428
- . spawn ( )
429
- . expect ( "could not run cargo" )
430
- . wait ( )
431
- . expect ( "failed to wait for cargo?" ) ;
432
-
433
- if exit_status. success ( ) {
434
- Ok ( ( ) )
400
+ // this conditional check for the --sysroot flag is there so users can call `cargo-miri` directly
401
+ // without having to pass --sysroot or anything
402
+ let rustc_args = std:: env:: args ( ) . skip ( 2 ) ;
403
+ let mut args: Vec < String > = if std:: env:: args ( ) . any ( |s| s == "--sysroot" ) {
404
+ rustc_args. collect ( )
405
+ } else {
406
+ rustc_args
407
+ . chain ( Some ( "--sysroot" . to_owned ( ) ) )
408
+ . chain ( Some ( sys_root) )
409
+ . collect ( )
410
+ } ;
411
+ args. splice ( 0 ..0 , miri:: miri_default_args ( ) . iter ( ) . map ( ToString :: to_string) ) ;
412
+
413
+ // see if we have cargo-miri-marker, which means we want to interpret this crate in Miri
414
+ // (and remove the marker).
415
+ let needs_miri = if let Some ( pos) = args. iter ( ) . position ( |arg| arg == "cargo-miri-marker" ) {
416
+ args. remove ( pos) ;
417
+ true
435
418
} else {
436
- Err ( exit_status. code ( ) . unwrap_or ( -1 ) )
419
+ false
420
+ } ;
421
+
422
+
423
+ let mut command = if needs_miri {
424
+ let mut path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
425
+ path. set_file_name ( "miri" ) ;
426
+ Command :: new ( path)
427
+ } else {
428
+ Command :: new ( "rustc" )
429
+ } ;
430
+ command. args ( & args) ;
431
+ if has_arg_flag ( "-v" ) {
432
+ eprintln ! ( "+ {:?}" , command) ;
433
+ }
434
+
435
+ match command. status ( ) {
436
+ Ok ( exit) => {
437
+ if !exit. success ( ) {
438
+ std:: process:: exit ( exit. code ( ) . unwrap_or ( 42 ) ) ;
439
+ }
440
+ }
441
+ Err ( ref e) if needs_miri => panic ! ( "error during miri run: {:?}" , e) ,
442
+ Err ( ref e) => panic ! ( "error during rustc call: {:?}" , e) ,
437
443
}
438
444
}
0 commit comments