@@ -6,7 +6,7 @@ use std::ops::Not;
6
6
use std:: path:: { Path , PathBuf } ;
7
7
use std:: process:: Command ;
8
8
9
- const XARGO_MIN_VERSION : ( u32 , u32 , u32 ) = ( 0 , 3 , 17 ) ;
9
+ const XARGO_MIN_VERSION : ( u32 , u32 , u32 ) = ( 0 , 3 , 19 ) ;
10
10
11
11
const CARGO_MIRI_HELP : & str = r#"Interprets bin crates and tests in Miri
12
12
@@ -23,7 +23,7 @@ Common options:
23
23
--features Features to compile for the package
24
24
-V, --version Print version info and exit
25
25
26
- Other [options] are the same as `cargo rustc `. Everything after the first "--" is
26
+ Other [options] are the same as `cargo check `. Everything after the first "--" is
27
27
passed verbatim to Miri, which will pass everything after the second "--" verbatim
28
28
to the interpreted program.
29
29
"# ;
@@ -84,6 +84,31 @@ fn get_arg_flag_value(name: &str) -> Option<String> {
84
84
}
85
85
}
86
86
87
+ /// Returns the path to the `miri` binary
88
+ fn find_miri ( ) -> PathBuf {
89
+ let mut path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
90
+ path. set_file_name ( "miri" ) ;
91
+ path
92
+ }
93
+
94
+ fn cargo ( ) -> Command {
95
+ if let Ok ( val) = std:: env:: var ( "CARGO" ) {
96
+ // Bootstrap tells us where to find cargo
97
+ Command :: new ( val)
98
+ } else {
99
+ Command :: new ( "cargo" )
100
+ }
101
+ }
102
+
103
+ fn xargo ( ) -> Command {
104
+ if let Ok ( val) = std:: env:: var ( "XARGO" ) {
105
+ // Bootstrap tells us where to find xargo
106
+ Command :: new ( val)
107
+ } else {
108
+ Command :: new ( "xargo-check" )
109
+ }
110
+ }
111
+
87
112
fn list_targets ( ) -> impl Iterator < Item = cargo_metadata:: Target > {
88
113
// We need to get the manifest, and then the metadata, to enumerate targets.
89
114
let manifest_path =
@@ -127,13 +152,6 @@ fn list_targets() -> impl Iterator<Item = cargo_metadata::Target> {
127
152
package. targets . into_iter ( )
128
153
}
129
154
130
- /// Returns the path to the `miri` binary
131
- fn find_miri ( ) -> PathBuf {
132
- let mut path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
133
- path. set_file_name ( "miri" ) ;
134
- path
135
- }
136
-
137
155
/// Make sure that the `miri` and `rustc` binary are from the same sysroot.
138
156
/// This can be violated e.g. when miri is locally built and installed with a different
139
157
/// toolchain than what is used when `cargo miri` is run.
@@ -183,24 +201,6 @@ fn test_sysroot_consistency() {
183
201
}
184
202
}
185
203
186
- fn cargo ( ) -> Command {
187
- if let Ok ( val) = std:: env:: var ( "CARGO" ) {
188
- // Bootstrap tells us where to find cargo
189
- Command :: new ( val)
190
- } else {
191
- Command :: new ( "cargo" )
192
- }
193
- }
194
-
195
- fn xargo ( ) -> Command {
196
- if let Ok ( val) = std:: env:: var ( "XARGO" ) {
197
- // Bootstrap tells us where to find xargo
198
- Command :: new ( val)
199
- } else {
200
- Command :: new ( "xargo" )
201
- }
202
- }
203
-
204
204
fn xargo_version ( ) -> Option < ( u32 , u32 , u32 ) > {
205
205
let out = xargo ( ) . arg ( "--version" ) . output ( ) . ok ( ) ?;
206
206
if !out. status . success ( ) {
@@ -357,6 +357,7 @@ features = ["panic_unwind"]
357
357
)
358
358
. unwrap ( ) ;
359
359
// The boring bits: a dummy project for xargo.
360
+ // FIXME: With xargo-check, can we avoid doing this?
360
361
File :: create ( dir. join ( "Cargo.toml" ) )
361
362
. unwrap ( )
362
363
. write_all (
@@ -419,12 +420,12 @@ fn main() {
419
420
}
420
421
421
422
if let Some ( "miri" ) = std:: env:: args ( ) . nth ( 1 ) . as_ref ( ) . map ( AsRef :: as_ref) {
422
- // This arm is for when `cargo miri` is called. We call `cargo rustc ` for each applicable target,
423
+ // This arm is for when `cargo miri` is called. We call `cargo check ` for each applicable target,
423
424
// but with the `RUSTC` env var set to the `cargo-miri` binary so that we come back in the other branch,
424
425
// and dispatch the invocations to `rustc` and `miri`, respectively.
425
426
in_cargo_miri ( ) ;
426
427
} else if let Some ( "rustc" ) = std:: env:: args ( ) . nth ( 1 ) . as_ref ( ) . map ( AsRef :: as_ref) {
427
- // This arm is executed when `cargo-miri` runs `cargo rustc ` with the `RUSTC_WRAPPER` env var set to itself:
428
+ // This arm is executed when `cargo-miri` runs `cargo check ` with the `RUSTC_WRAPPER` env var set to itself:
428
429
// dependencies get dispatched to `rustc`, the final test/binary to `miri`.
429
430
inside_cargo_rustc ( ) ;
430
431
} else {
@@ -463,11 +464,11 @@ fn in_cargo_miri() {
463
464
. kind
464
465
. get ( 0 )
465
466
. expect ( "badly formatted cargo metadata: target::kind is an empty array" ) ;
466
- // Now we run `cargo rustc $FLAGS $ARGS`, giving the user the
467
+ // Now we run `cargo check $FLAGS $ARGS`, giving the user the
467
468
// change to add additional arguments. `FLAGS` is set to identify
468
469
// this target. The user gets to control what gets actually passed to Miri.
469
470
let mut cmd = cargo ( ) ;
470
- cmd. arg ( "rustc " ) ;
471
+ cmd. arg ( "check " ) ;
471
472
match ( subcommand, kind. as_str ( ) ) {
472
473
( MiriCommand :: Run , "bin" ) => {
473
474
// FIXME: we just run all the binaries here.
@@ -487,20 +488,29 @@ fn in_cargo_miri() {
487
488
// The remaining targets we do not even want to build.
488
489
_ => continue ,
489
490
}
490
- // Add user-defined args until first `--`.
491
+ // Forward user-defined `cargo` args until first `--`.
491
492
while let Some ( arg) = args. next ( ) {
492
493
if arg == "--" {
493
494
break ;
494
495
}
495
496
cmd. arg ( arg) ;
496
497
}
497
- // Add `--` (to end the `cargo` flags), and then the user flags. We add markers around the
498
- // user flags to be able to identify them later. "cargo rustc" adds more stuff after this,
499
- // so we have to mark both the beginning and the end.
500
- cmd. arg ( "--" ) . arg ( "cargo-miri-marker-begin" ) . args ( args) . arg ( "cargo-miri-marker-end" ) ;
498
+
499
+ // Serialize the remaining args into a special environemt variable.
500
+ // This will be read by `inside_cargo_rustc` when we go to invoke
501
+ // our actual target crate (the binary or the test we are running).
502
+ // Since we're using "cargo check", we have no other way of passing
503
+ // these arguments.
504
+ let args_vec: Vec < String > = args. collect ( ) ;
505
+ cmd. env ( "MIRI_ARGS" , serde_json:: to_string ( & args_vec) . expect ( "failed to serialize args" ) ) ;
506
+
507
+ // Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
508
+ // i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
509
+ // the two codepaths.
501
510
let path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
502
511
cmd. env ( "RUSTC_WRAPPER" , path) ;
503
512
if verbose {
513
+ cmd. env ( "MIRI_VERBOSE" , "" ) ; // this makes `inside_cargo_rustc` verbose.
504
514
eprintln ! ( "+ {:?}" , cmd) ;
505
515
}
506
516
@@ -514,38 +524,71 @@ fn in_cargo_miri() {
514
524
}
515
525
516
526
fn inside_cargo_rustc ( ) {
517
- let sysroot = std:: env:: var ( "MIRI_SYSROOT" ) . expect ( "The wrapper should have set MIRI_SYSROOT" ) ;
518
-
519
- let rustc_args = std:: env:: args ( ) . skip ( 2 ) ; // skip `cargo rustc`
520
- let mut args: Vec < String > =
521
- rustc_args. chain ( Some ( "--sysroot" . to_owned ( ) ) ) . chain ( Some ( sysroot) ) . collect ( ) ;
522
- args. splice ( 0 ..0 , miri:: miri_default_args ( ) . iter ( ) . map ( ToString :: to_string) ) ;
523
-
524
- // See if we can find the `cargo-miri` markers. Those only get added to the binary we want to
525
- // run. They also serve to mark the user-defined arguments, which we have to move all the way
526
- // to the end (they get added somewhere in the middle).
527
- let needs_miri =
528
- if let Some ( begin) = args. iter ( ) . position ( |arg| arg == "cargo-miri-marker-begin" ) {
529
- let end = args
530
- . iter ( )
531
- . position ( |arg| arg == "cargo-miri-marker-end" )
532
- . expect ( "cannot find end marker" ) ;
533
- // These mark the user arguments. We remove the first and last as they are the markers.
534
- let mut user_args = args. drain ( begin..=end) ;
535
- assert_eq ! ( user_args. next( ) . unwrap( ) , "cargo-miri-marker-begin" ) ;
536
- assert_eq ! ( user_args. next_back( ) . unwrap( ) , "cargo-miri-marker-end" ) ;
537
- // Collect the rest and add it back at the end.
538
- let mut user_args = user_args. collect :: < Vec < String > > ( ) ;
527
+ /// Determines if we are being invoked (as rustc) to build a runnable
528
+ /// executable. We run "cargo check", so this should only happen when
529
+ /// we are trying to compile a build script or build script dependency,
530
+ /// which actually needs to be executed on the host platform.
531
+ ///
532
+ /// Currently, we detect this by checking for "--emit=link",
533
+ /// which indicates that Cargo instruced rustc to output
534
+ /// a native object.
535
+ fn is_target_crate ( ) -> bool {
536
+ // `--emit` is sometimes missing, e.g. cargo calls rustc for "--print".
537
+ // That is definitely not a target crate.
538
+ // If `--emit` is present, then host crates are built ("--emit=link,...),
539
+ // while the rest is only checked.
540
+ get_arg_flag_value ( "--emit" ) . map_or ( false , |emit| !emit. contains ( "link" ) )
541
+ }
542
+
543
+ /// Returns whether or not Cargo invoked the wrapper (this binary) to compile
544
+ /// the final, target crate (either a test for 'cargo test', or a binary for 'cargo run')
545
+ /// Cargo does not give us this information directly, so we need to check
546
+ /// various command-line flags.
547
+ fn is_runnable_crate ( ) -> bool {
548
+ let is_bin = get_arg_flag_value ( "--crate-type" ) . as_deref ( ) == Some ( "bin" ) ;
549
+ let is_test = has_arg_flag ( "--test" ) ;
550
+
551
+ // The final runnable (under Miri) crate will either be a binary crate
552
+ // or a test crate. We make sure to exclude build scripts here, since
553
+ // they are also build with "--crate-type bin"
554
+ is_bin || is_test
555
+ }
556
+
557
+ let verbose = std:: env:: var ( "MIRI_VERBOSE" ) . is_ok ( ) ;
558
+ let target_crate = is_target_crate ( ) ;
559
+
560
+ // Figure out which arguments we need to pass.
561
+ let mut args: Vec < String > = std:: env:: args ( ) . skip ( 2 ) . collect ( ) ; // skip `cargo-miri rustc`
562
+ // We make sure to only specify our custom Xargo sysroot and
563
+ // other args for target crates - that is, crates which are ultimately
564
+ // going to get interpreted by Miri.
565
+ if target_crate {
566
+ let sysroot = std:: env:: var ( "MIRI_SYSROOT" ) . expect ( "The wrapper should have set MIRI_SYSROOT" ) ;
567
+ args. push ( "--sysroot" . to_owned ( ) ) ;
568
+ args. push ( sysroot) ;
569
+ args. splice ( 0 ..0 , miri:: miri_default_args ( ) . iter ( ) . map ( ToString :: to_string) ) ;
570
+ }
571
+
572
+ // Figure out the binary we need to call. If this is a runnable target crate, we want to call
573
+ // Miri to start interpretation; otherwise we want to call rustc to build the crate as usual.
574
+ let mut command =
575
+ if target_crate && is_runnable_crate ( ) {
576
+ // This is the 'target crate' - the binary or test crate that
577
+ // we want to interpret under Miri. We deserialize the user-provided arguments
578
+ // from the special environment variable "MIRI_ARGS", and feed them
579
+ // to the 'miri' binary.
580
+ let magic = std:: env:: var ( "MIRI_ARGS" ) . expect ( "missing MIRI_ARGS" ) ;
581
+ let mut user_args: Vec < String > = serde_json:: from_str ( & magic) . expect ( "failed to deserialize MIRI_ARGS" ) ;
539
582
args. append ( & mut user_args) ;
540
583
// Run this in Miri.
541
- true
584
+ Command :: new ( find_miri ( ) )
542
585
} else {
543
- false
586
+ Command :: new ( "rustc" )
544
587
} ;
545
588
546
- let mut command = if needs_miri { Command :: new ( find_miri ( ) ) } else { Command :: new ( "rustc" ) } ;
589
+ // Run it.
547
590
command. args ( & args) ;
548
- if has_arg_flag ( "-v" ) {
591
+ if verbose {
549
592
eprintln ! ( "+ {:?}" , command) ;
550
593
}
551
594
@@ -554,7 +597,6 @@ fn inside_cargo_rustc() {
554
597
if !exit. success ( ) {
555
598
std:: process:: exit ( exit. code ( ) . unwrap_or ( 42 ) ) ;
556
599
} ,
557
- Err ( ref e) if needs_miri => panic ! ( "error during miri run: {:?}" , e) ,
558
- Err ( ref e) => panic ! ( "error during rustc call: {:?}" , e) ,
600
+ Err ( ref e) => panic ! ( "error running {:?}:\n {:?}" , command, e) ,
559
601
}
560
602
}
0 commit comments