@@ -9,7 +9,7 @@ use std::ffi::{OsStr, OsString};
9
9
use std:: fmt:: Write as _;
10
10
use std:: fs:: { self , File } ;
11
11
use std:: io:: { self , BufRead , BufReader , BufWriter , Read , Write } ;
12
- use std:: iter:: TakeWhile ;
12
+ use std:: iter:: { self , TakeWhile } ;
13
13
use std:: ops:: Not ;
14
14
use std:: path:: { Path , PathBuf } ;
15
15
use std:: process:: { self , Command } ;
@@ -35,6 +35,11 @@ The cargo options are exactly the same as for `cargo run` and `cargo test`, resp
35
35
Examples:
36
36
cargo miri run
37
37
cargo miri test -- test-suite-filter
38
+
39
+ cargo miri setup --print sysroot
40
+ This will print the path to the generated sysroot (and nothing else) on stdout.
41
+ stderr will still contain progress information about how the build is doing.
42
+
38
43
"# ;
39
44
40
45
#[ derive( Clone , Debug ) ]
@@ -206,6 +211,14 @@ fn forward_miri_sysroot(cmd: &mut Command) {
206
211
cmd. arg ( sysroot) ;
207
212
}
208
213
214
+ /// Escapes `s` in a way that is suitable for using it as a string literal in TOML syntax.
215
+ fn escape_for_toml ( s : & str ) -> String {
216
+ // We want to surround this string in quotes `"`. So we first escape all quotes,
217
+ // and also all backslashes (that are used to escape quotes).
218
+ let s = s. replace ( '\\' , r#"\\"# ) . replace ( '"' , r#"\""# ) ;
219
+ format ! ( "\" {}\" " , s)
220
+ }
221
+
209
222
/// Returns the path to the `miri` binary
210
223
fn find_miri ( ) -> PathBuf {
211
224
if let Some ( path) = env:: var_os ( "MIRI" ) {
@@ -353,17 +366,15 @@ fn write_to_file(filename: &Path, content: &str) {
353
366
/// done all this already.
354
367
fn setup ( subcommand : & MiriCommand ) {
355
368
let only_setup = matches ! ( subcommand, MiriCommand :: Setup ) ;
369
+ let ask_user = !only_setup;
370
+ let print_sysroot = only_setup && has_arg_flag ( "--print-sysroot" ) ; // whether we just print the sysroot path
356
371
if std:: env:: var_os ( "MIRI_SYSROOT" ) . is_some ( ) {
357
372
if only_setup {
358
373
println ! ( "WARNING: MIRI_SYSROOT already set, not doing anything." )
359
374
}
360
375
return ;
361
376
}
362
377
363
- // Subcommands other than `setup` will do a setup if necessary, but
364
- // interactively confirm first.
365
- let ask_user = !only_setup;
366
-
367
378
// First, we need xargo.
368
379
if xargo_version ( ) . map_or ( true , |v| v < XARGO_MIN_VERSION ) {
369
380
if std:: env:: var_os ( "XARGO_CHECK" ) . is_some ( ) {
@@ -499,8 +510,14 @@ path = "lib.rs"
499
510
command. env ( "RUSTFLAGS" , "-Cdebug-assertions=off -Coverflow-checks=on" ) ;
500
511
// Manage the output the user sees.
501
512
if only_setup {
513
+ // We want to be explicit.
502
514
eprintln ! ( "Preparing a sysroot for Miri..." ) ;
515
+ if print_sysroot {
516
+ // Be extra sure there is no noise on stdout.
517
+ command. stdout ( process:: Stdio :: null ( ) ) ;
518
+ }
503
519
} else {
520
+ // We want to be quiet, but still let the user know that something is happening.
504
521
eprint ! ( "Preparing a sysroot for Miri... " ) ;
505
522
command. stdout ( process:: Stdio :: null ( ) ) ;
506
523
command. stderr ( process:: Stdio :: null ( ) ) ;
@@ -515,22 +532,21 @@ path = "lib.rs"
515
532
) )
516
533
}
517
534
}
518
- if !only_setup {
519
- eprintln ! ( "done" ) ;
520
- }
521
535
522
536
// That should be it! But we need to figure out where xargo built stuff.
523
537
// Unfortunately, it puts things into a different directory when the
524
538
// architecture matches the host.
525
539
let sysroot = if target == & host { dir. join ( "HOST" ) } else { PathBuf :: from ( dir) } ;
526
540
std:: env:: set_var ( "MIRI_SYSROOT" , & sysroot) ; // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
527
541
// Figure out what to print.
528
- let print_sysroot = only_setup && has_arg_flag ( "--print-sysroot" ) ; // whether we just print the sysroot path
542
+ if only_setup {
543
+ eprintln ! ( "A sysroot for Miri is now available in `{}`." , sysroot. display( ) ) ;
544
+ } else {
545
+ eprintln ! ( "done" ) ;
546
+ }
529
547
if print_sysroot {
530
548
// Print just the sysroot and nothing else to stdout; this way we do not need any escaping.
531
549
println ! ( "{}" , sysroot. display( ) ) ;
532
- } else if only_setup {
533
- eprintln ! ( "A sysroot for Miri is now available in `{}`." , sysroot. display( ) ) ;
534
550
}
535
551
}
536
552
@@ -669,7 +685,11 @@ fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
669
685
// <https://github.com/rust-lang/miri/pull/1540#issuecomment-693553191> describes an alternative
670
686
// approach that uses `cargo check`, making that part easier but target and binary handling
671
687
// harder.
672
- let cargo_miri_path = std:: env:: current_exe ( ) . expect ( "current executable path invalid" ) ;
688
+ let cargo_miri_path = std:: env:: current_exe ( )
689
+ . expect ( "current executable path invalid" )
690
+ . into_os_string ( )
691
+ . into_string ( )
692
+ . expect ( "current executable path is not valid UTF-8" ) ;
673
693
let cargo_cmd = match subcommand {
674
694
MiriCommand :: Forward ( s) => s,
675
695
MiriCommand :: Setup => return , // `cargo miri setup` stops here.
@@ -699,20 +719,21 @@ fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
699
719
target_dir. push ( "miri" ) ;
700
720
cmd. arg ( "--target-dir" ) . arg ( target_dir) ;
701
721
702
- // Make sure we know the build target, and cargo does, too .
703
- // This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something,
722
+ // Make sure the build target is explicitly set .
723
+ // This is needed to make the `target.runner` settings do something,
704
724
// and it later helps us detect which crates are proc-macro/build-script
705
725
// (host crates) and which crates are needed for the program itself.
706
- let host = version_info ( ) . host ;
707
- let target = get_arg_flag_value ( "--target" ) ;
708
- let target = if let Some ( ref target) = target {
709
- target
710
- } else {
711
- // No target given. Pick default and tell cargo about it.
726
+ if get_arg_flag_value ( "--target" ) . is_none ( ) {
727
+ // No target given. Explicitly pick the host.
712
728
cmd. arg ( "--target" ) ;
713
- cmd. arg ( & host) ;
714
- & host
715
- } ;
729
+ cmd. arg ( version_info ( ) . host ) ;
730
+ }
731
+
732
+ // Set ourselves as runner for al binaries invoked by cargo.
733
+ // We use `all()` since `true` is not a thing in cfg-lang, but the empty conjunction is. :)
734
+ let cargo_miri_path_for_toml = escape_for_toml ( & cargo_miri_path) ;
735
+ cmd. arg ( "--config" )
736
+ . arg ( format ! ( "target.'cfg(all())'.runner=[{cargo_miri_path_for_toml}, 'runner']" ) ) ;
716
737
717
738
// Forward all further arguments after `--` to cargo.
718
739
cmd. arg ( "--" ) . args ( args) ;
@@ -743,16 +764,6 @@ fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
743
764
// bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host builds.
744
765
cmd. env ( "RUSTC" , & fs:: canonicalize ( find_miri ( ) ) . unwrap ( ) ) ;
745
766
746
- let runner_env_name =
747
- |triple : & str | format ! ( "CARGO_TARGET_{}_RUNNER" , triple. to_uppercase( ) . replace( '-' , "_" ) ) ;
748
- let host_runner_env_name = runner_env_name ( & host) ;
749
- let target_runner_env_name = runner_env_name ( target) ;
750
- // Set the target runner to us, so we can interpret the binaries.
751
- cmd. env ( & target_runner_env_name, & cargo_miri_path) ;
752
- // Unit tests of `proc-macro` crates are run on the host, so we set the host runner to
753
- // us in order to skip them.
754
- cmd. env ( & host_runner_env_name, & cargo_miri_path) ;
755
-
756
767
// Set rustdoc to us as well, so we can run doctests.
757
768
cmd. env ( "RUSTDOC" , & cargo_miri_path) ;
758
769
@@ -1194,38 +1205,25 @@ fn main() {
1194
1205
return ;
1195
1206
}
1196
1207
1197
- let mut args = args. peekable ( ) ;
1198
- if args. next_if ( |a| a == "miri" ) . is_some ( ) {
1199
- phase_cargo_miri ( args) ;
1200
- } else if let Some ( arg) = args. peek ( ) . cloned ( ) {
1201
- // Cargo calls us for everything it does. We could be invoked as rustc, rustdoc, or the runner.
1202
-
1203
- // If the first arg is equal to the RUSTC variable (which should be set at this point),
1204
- // then we need to behave as rustc. This is the somewhat counter-intuitive behavior of
1205
- // having both RUSTC and RUSTC_WRAPPER set (see
1206
- // https://github.com/rust-lang/cargo/issues/10886).
1207
- if arg == env:: var ( "RUSTC" ) . unwrap ( ) {
1208
- args. next ( ) . unwrap ( ) ; // consume wrapped RUSTC command.
1209
- return phase_rustc ( args, RustcPhase :: Build ) ;
1210
- }
1211
- // We have to distinguish the "runner" and "rustdoc" cases.
1212
- // As runner, the first argument is the binary (a file that should exist, with an absolute path);
1213
- // as rustdoc, the first argument is a flag (`--something`).
1214
- let binary = Path :: new ( & arg) ;
1215
- if binary. exists ( ) {
1216
- assert ! ( !arg. starts_with( "--" ) ) ; // not a flag
1217
- phase_runner ( args, RunnerPhase :: Cargo ) ;
1218
- } else if arg. starts_with ( "--" ) {
1219
- phase_rustdoc ( args) ;
1220
- } else {
1221
- show_error ( format ! (
1222
- "`cargo-miri` called with unexpected first argument `{}`; please only invoke this binary through `cargo miri`" ,
1223
- arg
1224
- ) ) ;
1225
- }
1226
- } else {
1208
+ let Some ( first) = args. next ( ) else {
1227
1209
show_error ( format ! (
1228
1210
"`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`"
1229
- ) ) ;
1211
+ ) )
1212
+ } ;
1213
+ match first. as_str ( ) {
1214
+ "miri" => phase_cargo_miri ( args) ,
1215
+ "runner" => phase_runner ( args, RunnerPhase :: Cargo ) ,
1216
+ arg if arg == env:: var ( "RUSTC" ) . unwrap ( ) => {
1217
+ // If the first arg is equal to the RUSTC env ariable (which should be set at this
1218
+ // point), then we need to behave as rustc. This is the somewhat counter-intuitive
1219
+ // behavior of having both RUSTC and RUSTC_WRAPPER set
1220
+ // (see https://github.com/rust-lang/cargo/issues/10886).
1221
+ phase_rustc ( args, RustcPhase :: Build )
1222
+ }
1223
+ _ => {
1224
+ // Everything else must be rustdoc. But we need to get `first` "back onto the iterator",
1225
+ // it is some part of the rustdoc invocation.
1226
+ phase_rustdoc ( iter:: once ( first) . chain ( args) ) ;
1227
+ }
1230
1228
}
1231
1229
}
0 commit comments