2020-04-23 10:46:49 -05:00
use std ::env ;
2020-05-21 04:32:12 -05:00
use std ::ffi ::OsString ;
2019-02-15 19:29:38 -06:00
use std ::fs ::{ self , File } ;
2020-09-07 13:19:45 -05:00
use std ::io ::{ self , BufRead , BufReader , BufWriter , Write } ;
2019-08-04 03:14:51 -05:00
use std ::ops ::Not ;
2019-12-23 05:56:23 -06:00
use std ::path ::{ Path , PathBuf } ;
use std ::process ::Command ;
2020-09-07 13:19:45 -05:00
use serde ::{ Deserialize , Serialize } ;
2017-01-24 06:28:36 -06:00
2020-05-10 11:41:03 -05:00
use rustc_version ::VersionMeta ;
2020-07-28 07:00:33 -05:00
const XARGO_MIN_VERSION : ( u32 , u32 , u32 ) = ( 0 , 3 , 22 ) ;
2019-11-08 09:36:57 -06:00
2019-02-07 06:00:42 -06:00
const CARGO_MIRI_HELP : & str = r #" Interprets bin crates and tests in Miri
2017-01-24 06:28:36 -06:00
Usage :
2020-05-17 03:08:45 -05:00
cargo miri [ subcommand ] [ < cargo options > .. . ] [ - - ] [ < miri options > .. . ] [ - - ] [ < program / test suite options > .. . ]
2018-11-25 09:30:11 -06:00
Subcommands :
run Run binaries ( default )
test Run tests
setup Only perform automatic setup , but without asking questions ( for getting a proper libstd )
2017-01-24 06:28:36 -06:00
Common options :
- h , - - help Print this message
- - features Features to compile for the package
- V , - - version Print version info and exit
2020-02-24 07:17:19 -06:00
Other [ options ] are the same as ` cargo check ` . Everything after the first " -- " is
2019-02-09 05:42:16 -06:00
passed verbatim to Miri , which will pass everything after the second " -- " verbatim
to the interpreted program .
2020-05-17 03:08:45 -05:00
Examples :
cargo miri run - - - Zmiri - disable - stacked - borrows
cargo miri test - - - - test - suite - filter
2017-01-24 06:28:36 -06:00
" #;
2018-11-25 09:30:11 -06:00
#[ derive(Copy, Clone, Debug, PartialEq, Eq) ]
2018-11-25 09:08:24 -06:00
enum MiriCommand {
Run ,
Test ,
Setup ,
}
2020-09-07 13:19:45 -05:00
/// The inforamtion Miri needs to run a crate. Stored as JSON when the crate is "compiled".
#[ derive(Serialize, Deserialize) ]
struct CrateRunInfo {
/// The command-line arguments.
2020-09-09 01:58:29 -05:00
args : Vec < String > ,
2020-09-07 13:19:45 -05:00
/// The environment.
2020-09-07 14:12:51 -05:00
env : Vec < ( OsString , OsString ) > ,
}
impl CrateRunInfo {
/// Gather all the information we need.
2020-09-09 01:58:29 -05:00
fn collect ( args : env ::Args ) -> Self {
2020-09-07 14:12:51 -05:00
let args = args . collect ( ) ;
let env = env ::vars_os ( ) . collect ( ) ;
CrateRunInfo { args , env }
}
2020-09-08 18:00:09 -05:00
fn store ( & self , filename : & Path ) {
let file = File ::create ( filename )
. unwrap_or_else ( | _ | show_error ( format! ( " Cannot create ` {} ` " , filename . display ( ) ) ) ) ;
let file = BufWriter ::new ( file ) ;
serde_json ::ser ::to_writer ( file , self )
. unwrap_or_else ( | _ | show_error ( format! ( " Cannot write to ` {} ` " , filename . display ( ) ) ) ) ;
}
2020-09-07 13:19:45 -05:00
}
2017-01-24 06:28:36 -06:00
fn show_help ( ) {
println! ( " {} " , CARGO_MIRI_HELP ) ;
}
fn show_version ( ) {
2019-12-23 05:56:23 -06:00
println! (
" miri {} ({} {}) " ,
env! ( " CARGO_PKG_VERSION " ) ,
env! ( " VERGEN_SHA_SHORT " ) ,
env! ( " VERGEN_COMMIT_DATE " )
) ;
2017-01-24 06:28:36 -06:00
}
2018-11-25 09:30:11 -06:00
fn show_error ( msg : String ) -> ! {
eprintln! ( " fatal error: {} " , msg ) ;
std ::process ::exit ( 1 )
}
2019-02-15 19:29:38 -06:00
// Determines whether a `--flag` is present.
2019-02-07 06:00:27 -06:00
fn has_arg_flag ( name : & str ) -> bool {
let mut args = std ::env ::args ( ) . take_while ( | val | val ! = " -- " ) ;
args . any ( | val | val = = name )
}
2019-02-15 19:29:38 -06:00
/// Gets the value of a `--flag`.
2018-12-10 02:23:27 -06:00
fn get_arg_flag_value ( name : & str ) -> Option < String > {
2019-02-15 19:29:38 -06:00
// Stop searching at `--`.
2019-02-07 06:00:27 -06:00
let mut args = std ::env ::args ( ) . take_while ( | val | val ! = " -- " ) ;
loop {
let arg = match args . next ( ) {
Some ( arg ) = > arg ,
None = > return None ,
} ;
if ! arg . starts_with ( name ) {
continue ;
}
2019-02-15 19:29:38 -06:00
// Strip leading `name`.
let suffix = & arg [ name . len ( ) .. ] ;
2019-02-07 06:00:27 -06:00
if suffix . is_empty ( ) {
2019-02-15 19:29:38 -06:00
// This argument is exactly `name`; the next one is the value.
2019-02-07 06:00:27 -06:00
return args . next ( ) ;
} else if suffix . starts_with ( '=' ) {
2019-02-15 19:29:38 -06:00
// This argument is `name=value`; get the value.
// Strip leading `=`.
return Some ( suffix [ 1 .. ] . to_owned ( ) ) ;
2019-02-07 06:00:27 -06:00
}
2018-12-10 02:23:27 -06:00
}
}
2020-05-09 05:24:30 -05:00
/// Returns the path to the `miri` binary
fn find_miri ( ) -> PathBuf {
2020-05-21 07:24:41 -05:00
if let Some ( path ) = env ::var_os ( " MIRI " ) {
return path . into ( ) ;
}
2020-02-24 07:17:19 -06:00
let mut path = std ::env ::current_exe ( ) . expect ( " current executable path invalid " ) ;
path . set_file_name ( " miri " ) ;
2020-05-09 05:24:30 -05:00
path
}
fn miri ( ) -> Command {
Command ::new ( find_miri ( ) )
2020-01-01 03:12:27 -06:00
}
2020-05-10 11:41:03 -05:00
fn version_info ( ) -> VersionMeta {
VersionMeta ::for_command ( miri ( ) ) . expect ( " failed to determine underlying rustc version of Miri " )
}
2020-02-24 07:17:19 -06:00
fn cargo ( ) -> Command {
2020-05-09 04:52:26 -05:00
Command ::new ( env ::var_os ( " CARGO " ) . unwrap_or_else ( | | OsString ::from ( " cargo " ) ) )
2020-01-01 03:12:27 -06:00
}
2020-03-02 15:10:48 -06:00
fn xargo_check ( ) -> Command {
2020-05-09 04:52:26 -05:00
Command ::new ( env ::var_os ( " XARGO_CHECK " ) . unwrap_or_else ( | | OsString ::from ( " xargo-check " ) ) )
}
2020-09-08 13:08:11 -05:00
/// Execute the command if it fails, fail this process with the same exit code.
/// Otherwise, continue.
fn exec ( mut cmd : Command ) {
2020-09-07 14:12:51 -05:00
let exit_status = cmd . status ( ) . expect ( " failed to run command " ) ;
2020-09-08 13:08:11 -05:00
if exit_status . success ( ) . not ( ) {
std ::process ::exit ( exit_status . code ( ) . unwrap_or ( - 1 ) )
}
2020-09-07 14:12:51 -05:00
}
2018-12-15 08:08:03 -06:00
fn xargo_version ( ) -> Option < ( u32 , u32 , u32 ) > {
2020-03-02 15:10:48 -06:00
let out = xargo_check ( ) . arg ( " --version " ) . output ( ) . ok ( ) ? ;
2018-12-15 08:08:03 -06:00
if ! out . status . success ( ) {
return None ;
}
// Parse output. The first line looks like "xargo 0.3.12 (b004f1c 2018-12-13)".
2019-12-23 05:56:23 -06:00
let line = out
. stderr
. lines ( )
. nth ( 0 )
2018-12-15 08:08:03 -06:00
. expect ( " malformed `xargo --version` output: not at least one line " )
. expect ( " malformed `xargo --version` output: error reading first line " ) ;
2018-12-19 01:41:31 -06:00
let ( name , version ) = {
let mut split = line . split ( ' ' ) ;
2019-12-23 05:56:23 -06:00
(
split . next ( ) . expect ( " malformed `xargo --version` output: empty " ) ,
split . next ( ) . expect ( " malformed `xargo --version` output: not at least two words " ) ,
)
2018-12-19 01:41:31 -06:00
} ;
2019-05-10 01:05:34 -05:00
if name ! = " xargo " {
// This is some fork of xargo
2019-05-01 13:37:08 -05:00
return None ;
}
2018-12-15 08:08:03 -06:00
let mut version_pieces = version . split ( '.' ) ;
2019-12-23 05:56:23 -06:00
let major = version_pieces
. next ( )
2018-12-15 08:08:03 -06:00
. expect ( " malformed `xargo --version` output: not a major version piece " )
. parse ( )
. expect ( " malformed `xargo --version` output: major version is not an integer " ) ;
2019-12-23 05:56:23 -06:00
let minor = version_pieces
. next ( )
2018-12-15 08:08:03 -06:00
. expect ( " malformed `xargo --version` output: not a minor version piece " )
. parse ( )
. expect ( " malformed `xargo --version` output: minor version is not an integer " ) ;
2019-12-23 05:56:23 -06:00
let patch = version_pieces
. next ( )
2018-12-15 08:08:03 -06:00
. expect ( " malformed `xargo --version` output: not a patch version piece " )
. parse ( )
. expect ( " malformed `xargo --version` output: patch version is not an integer " ) ;
if ! version_pieces . next ( ) . is_none ( ) {
panic! ( " malformed `xargo --version` output: more than three pieces in version " ) ;
}
Some ( ( major , minor , patch ) )
}
2020-04-23 12:27:38 -05:00
fn ask_to_run ( mut cmd : Command , ask : bool , text : & str ) {
2020-04-23 12:12:48 -05:00
// Disable interactive prompts in CI (GitHub Actions, Travis, AppVeyor, etc).
2020-09-13 14:10:29 -05:00
// Azure doesn't set `CI` though (nothing to see here, just Microsoft being Microsoft),
// so we also check their `TF_BUILD`.
let is_ci = env ::var_os ( " CI " ) . is_some ( ) | | env ::var_os ( " TF_BUILD " ) . is_some ( ) ;
if ask & & ! is_ci {
2019-09-13 03:39:36 -05:00
let mut buf = String ::new ( ) ;
print! ( " I will run ` {:?} ` to {} . Proceed? [Y/n] " , cmd , text ) ;
io ::stdout ( ) . flush ( ) . unwrap ( ) ;
io ::stdin ( ) . read_line ( & mut buf ) . unwrap ( ) ;
match buf . trim ( ) . to_lowercase ( ) . as_ref ( ) {
// Proceed.
2019-12-23 05:56:23 -06:00
" " | " y " | " yes " = > { }
2019-09-13 03:39:36 -05:00
" n " | " no " = > show_error ( format! ( " Aborting as per your request " ) ) ,
2019-12-23 05:56:23 -06:00
a = > show_error ( format! ( " I do not understand ` {} ` " , a ) ) ,
2019-09-13 03:39:36 -05:00
} ;
} else {
println! ( " Running ` {:?} ` to {} . " , cmd , text ) ;
}
2019-12-23 05:56:23 -06:00
if cmd . status ( ) . expect ( & format! ( " failed to execute {:?} " , cmd ) ) . success ( ) . not ( ) {
2019-09-13 03:39:36 -05:00
show_error ( format! ( " Failed to {} " , text ) ) ;
}
2018-11-25 09:30:11 -06:00
}
2019-02-15 19:29:38 -06:00
/// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
/// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
/// done all this already.
2020-04-23 12:12:48 -05:00
fn setup ( subcommand : MiriCommand ) {
2020-05-09 04:58:18 -05:00
if std ::env ::var_os ( " MIRI_SYSROOT " ) . is_some ( ) {
2020-04-23 12:12:48 -05:00
if subcommand = = MiriCommand ::Setup {
2019-04-19 12:27:19 -05:00
println! ( " WARNING: MIRI_SYSROOT already set, not doing anything. " )
}
2018-11-25 09:30:11 -06:00
return ;
}
2020-04-23 12:27:38 -05:00
// Subcommands other than `setup` will do a setup if necessary, but
// interactively confirm first.
let ask_user = subcommand ! = MiriCommand ::Setup ;
2019-02-15 19:29:38 -06:00
// First, we need xargo.
2019-11-08 09:36:57 -06:00
if xargo_version ( ) . map_or ( true , | v | v < XARGO_MIN_VERSION ) {
2020-05-09 04:58:18 -05:00
if std ::env ::var_os ( " XARGO_CHECK " ) . is_some ( ) {
2019-10-21 03:25:47 -05:00
// The user manually gave us a xargo binary; don't do anything automatically.
show_error ( format! ( " Your xargo is too old; please upgrade to the latest version " ) )
}
2019-09-13 03:39:36 -05:00
let mut cmd = cargo ( ) ;
cmd . args ( & [ " install " , " xargo " , " -f " ] ) ;
2020-04-23 12:27:38 -05:00
ask_to_run ( cmd , ask_user , " install a recent enough xargo " ) ;
2018-11-25 09:30:11 -06:00
}
2018-11-25 10:09:49 -06:00
2019-11-23 03:33:49 -06:00
// Determine where the rust sources are located. `XARGO_RUST_SRC` env var trumps everything.
2020-05-09 04:58:18 -05:00
let rust_src = match std ::env ::var_os ( " XARGO_RUST_SRC " ) {
2020-07-09 06:02:42 -05:00
Some ( path ) = > {
2020-07-11 04:07:17 -05:00
let path = PathBuf ::from ( path ) ;
// Make path absolute if possible.
path . canonicalize ( ) . unwrap_or ( path )
2020-07-05 13:01:12 -05:00
}
2020-05-09 04:58:18 -05:00
None = > {
2019-11-23 03:33:49 -06:00
// Check for `rust-src` rustup component.
2020-05-10 11:41:03 -05:00
let sysroot = miri ( )
2019-12-23 05:56:23 -06:00
. args ( & [ " --print " , " sysroot " ] )
. output ( )
2020-05-10 11:41:03 -05:00
. expect ( " failed to determine sysroot " )
2019-11-23 03:33:49 -06:00
. stdout ;
let sysroot = std ::str ::from_utf8 ( & sysroot ) . unwrap ( ) ;
let sysroot = Path ::new ( sysroot . trim_end_matches ( '\n' ) ) ;
2020-07-28 07:00:33 -05:00
// Check for `$SYSROOT/lib/rustlib/src/rust/library`; test if that contains `std/Cargo.toml`.
2020-05-21 04:32:12 -05:00
let rustup_src =
2020-07-28 07:00:33 -05:00
sysroot . join ( " lib " ) . join ( " rustlib " ) . join ( " src " ) . join ( " rust " ) . join ( " library " ) ;
if ! rustup_src . join ( " std " ) . join ( " Cargo.toml " ) . exists ( ) {
2020-04-04 05:07:22 -05:00
// Ask the user to install the `rust-src` component, and use that.
let mut cmd = Command ::new ( " rustup " ) ;
cmd . args ( & [ " component " , " add " , " rust-src " ] ) ;
ask_to_run (
cmd ,
2020-04-23 12:27:38 -05:00
ask_user ,
2020-05-10 11:41:03 -05:00
" install the `rust-src` component for the selected toolchain " ,
2020-04-04 05:07:22 -05:00
) ;
}
rustup_src
2018-11-25 10:09:49 -06:00
}
2019-11-23 03:33:49 -06:00
} ;
if ! rust_src . exists ( ) {
show_error ( format! ( " Given Rust source directory ` {} ` does not exist. " , rust_src . display ( ) ) ) ;
2018-11-25 10:09:49 -06:00
}
2020-05-09 05:24:30 -05:00
// Next, we need our own libstd. Prepare a xargo project for that purpose.
// We will do this work in whatever is a good cache dir for this platform.
2019-06-20 12:45:39 -05:00
let dirs = directories ::ProjectDirs ::from ( " org " , " rust-lang " , " miri " ) . unwrap ( ) ;
2018-11-27 04:26:53 -06:00
let dir = dirs . cache_dir ( ) ;
2018-11-25 10:09:49 -06:00
if ! dir . exists ( ) {
2018-11-27 04:43:02 -06:00
fs ::create_dir_all ( & dir ) . unwrap ( ) ;
2018-11-25 10:09:49 -06:00
}
// The interesting bit: Xargo.toml
2019-12-23 05:56:23 -06:00
File ::create ( dir . join ( " Xargo.toml " ) )
. unwrap ( )
. write_all (
br #"
2018-11-25 10:09:49 -06:00
[ dependencies . std ]
2018-12-02 07:03:29 -06:00
default_features = false
# We need the ` panic_unwind ` feature because we use the ` unwind ` panic strategy .
# Using ` abort ` works for libstd , but then libtest will not compile .
2019-09-16 15:22:54 -05:00
features = [ " panic_unwind " ]
2018-11-25 10:09:49 -06:00
[ dependencies . test ]
2020-03-01 04:53:34 -06:00
" #,
2019-12-23 05:56:23 -06:00
)
. unwrap ( ) ;
2019-02-15 19:29:38 -06:00
// The boring bits: a dummy project for xargo.
2020-02-24 07:17:19 -06:00
// FIXME: With xargo-check, can we avoid doing this?
2019-12-23 05:56:23 -06:00
File ::create ( dir . join ( " Cargo.toml " ) )
. unwrap ( )
. write_all (
br #"
2018-11-25 10:09:49 -06:00
[ package ]
name = " miri-xargo "
description = " A dummy project for building libstd with xargo. "
version = " 0.0.0 "
[ lib ]
path = " lib.rs "
2020-03-01 04:53:34 -06:00
" #,
2019-12-23 05:56:23 -06:00
)
. unwrap ( ) ;
2018-11-25 10:09:49 -06:00
File ::create ( dir . join ( " lib.rs " ) ) . unwrap ( ) ;
2020-05-09 05:24:30 -05:00
// Determine architectures.
// We always need to set a target so rustc bootstrap can tell apart host from target crates.
2020-05-10 11:41:03 -05:00
let host = version_info ( ) . host ;
2018-12-10 02:32:54 -06:00
let target = get_arg_flag_value ( " --target " ) ;
2020-05-09 05:24:30 -05:00
let target = target . as_ref ( ) . unwrap_or ( & host ) ;
// Now invoke xargo.
2020-03-02 15:10:48 -06:00
let mut command = xargo_check ( ) ;
2020-07-29 11:21:32 -05:00
command . arg ( " check " ) . arg ( " -q " ) ;
2020-05-09 05:24:30 -05:00
command . arg ( " --target " ) . arg ( target ) ;
2019-08-03 10:20:16 -05:00
command . current_dir ( & dir ) ;
2019-11-23 03:33:49 -06:00
command . env ( " XARGO_HOME " , & dir ) ;
command . env ( " XARGO_RUST_SRC " , & rust_src ) ;
2020-05-09 05:24:30 -05:00
// Use Miri as rustc to build a libstd compatible with us (and use the right flags).
// However, when we are running in bootstrap, we cannot just overwrite `RUSTC`,
// because we still need bootstrap to distinguish between host and target crates.
// In that case we overwrite `RUSTC_REAL` instead which determines the rustc used
// for target crates.
if env ::var_os ( " RUSTC_STAGE " ) . is_some ( ) {
command . env ( " RUSTC_REAL " , find_miri ( ) ) ;
} else {
command . env ( " RUSTC " , find_miri ( ) ) ;
2018-12-10 02:32:54 -06:00
}
2020-05-09 05:24:30 -05:00
command . env ( " MIRI_BE_RUSTC " , " 1 " ) ;
2020-05-22 03:12:32 -05:00
// Make sure there are no other wrappers or flags getting in our way
// (Cc https://github.com/rust-lang/miri/issues/1421).
// This is consistent with normal `cargo build` that does not apply `RUSTFLAGS`
// to the sysroot either.
command . env_remove ( " RUSTC_WRAPPER " ) ;
command . env_remove ( " RUSTFLAGS " ) ;
2019-08-03 10:20:16 -05:00
// Finally run it!
2019-12-23 05:56:23 -06:00
if command . status ( ) . expect ( " failed to run xargo " ) . success ( ) . not ( ) {
2018-11-25 10:09:49 -06:00
show_error ( format! ( " Failed to run xargo " ) ) ;
}
2019-02-15 19:29:38 -06:00
// That should be it! But we need to figure out where xargo built stuff.
2018-12-10 02:32:54 -06:00
// Unfortunately, it puts things into a different directory when the
// architecture matches the host.
2020-05-09 05:24:30 -05:00
let sysroot = if target = = & host { dir . join ( " HOST " ) } else { PathBuf ::from ( dir ) } ;
2019-06-09 10:10:04 -05:00
std ::env ::set_var ( " MIRI_SYSROOT " , & sysroot ) ; // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
2020-05-09 05:24:30 -05:00
// Figure out what to print.
2020-05-21 04:32:12 -05:00
let print_sysroot = subcommand = = MiriCommand ::Setup & & has_arg_flag ( " --print-sysroot " ) ; // whether we just print the sysroot path
2019-10-19 09:36:45 -05:00
if print_sysroot {
// Print just the sysroot and nothing else; this way we do not need any escaping.
println! ( " {} " , sysroot . display ( ) ) ;
2020-04-23 12:12:48 -05:00
} else if subcommand = = MiriCommand ::Setup {
2019-06-09 06:48:18 -05:00
println! ( " A libstd for Miri is now available in ` {} `. " , sysroot . display ( ) ) ;
2018-11-30 02:23:44 -06:00
}
2018-11-25 09:30:11 -06:00
}
2020-09-09 01:58:29 -05:00
fn phase_cargo_miri ( mut args : env ::Args ) {
2020-09-09 01:51:41 -05:00
// Check for version and help flags even when invoked as `cargo-miri`.
if has_arg_flag ( " --help " ) | | has_arg_flag ( " -h " ) {
show_help ( ) ;
return ;
}
if has_arg_flag ( " --version " ) | | has_arg_flag ( " -V " ) {
show_version ( ) ;
return ;
}
2020-09-06 12:28:58 -05:00
// Require a subcommand before any flags.
// We cannot know which of those flags take arguments and which do not,
// so we cannot detect subcommands later.
2020-09-09 01:58:29 -05:00
let subcommand = match args . next ( ) . as_deref ( ) {
2020-09-06 12:28:58 -05:00
Some ( " test " ) = > MiriCommand ::Test ,
Some ( " run " ) = > MiriCommand ::Run ,
Some ( " setup " ) = > MiriCommand ::Setup ,
2019-02-15 19:29:38 -06:00
// Invalid command.
2020-09-07 14:12:51 -05:00
_ = > show_error ( format! ( " `cargo miri` must be immediately followed by `test`, `run`, or `setup`. " ) ) ,
2019-02-07 06:00:27 -06:00
} ;
let verbose = has_arg_flag ( " -v " ) ;
2017-01-24 06:28:36 -06:00
2019-02-15 19:29:38 -06:00
// We always setup.
2020-04-23 12:12:48 -05:00
setup ( subcommand ) ;
2017-01-24 06:28:36 -06:00
2020-09-06 12:28:58 -05:00
// Invoke actual cargo for the job, but with different flags.
let miri_path = std ::env ::current_exe ( ) . expect ( " current executable path invalid " ) ;
let cargo_cmd = match subcommand {
MiriCommand ::Test = > " test " ,
MiriCommand ::Run = > " run " ,
MiriCommand ::Setup = > return , // `cargo miri setup` stops here.
} ;
let mut cmd = cargo ( ) ;
cmd . arg ( cargo_cmd ) ;
// Make sure we know the build target, and cargo does, too.
// This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something,
// and it later helps us detect which crates are proc-macro/build-script
// (host crates) and which crates are needed for the program itself.
let target = if let Some ( target ) = get_arg_flag_value ( " --target " ) {
target
} else {
// No target given. Pick default and tell cargo about it.
let host = version_info ( ) . host ;
cmd . arg ( " --target " ) ;
cmd . arg ( & host ) ;
host
} ;
2020-01-01 03:12:27 -06:00
2020-09-06 12:28:58 -05:00
// Forward all further arguments.
cmd . args ( args ) ;
2019-02-07 06:00:27 -06:00
2020-09-06 12:28:58 -05:00
// Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation,
// i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish
// the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.)
if env ::var_os ( " RUSTC_WRAPPER " ) . is_some ( ) {
println! ( " WARNING: Ignoring `RUSTC_WRAPPER` environment variable, Miri does not support wrapping. " ) ;
}
cmd . env ( " RUSTC_WRAPPER " , & miri_path ) ;
if verbose {
eprintln! ( " + RUSTC_WRAPPER= {:?} " , miri_path ) ;
}
2019-02-07 06:00:27 -06:00
2020-09-06 12:28:58 -05:00
// Set the runner for the current target to us as well, so we can interpret the binaries.
let runner_env_name = format! ( " CARGO_TARGET_ {} _RUNNER " , target . to_uppercase ( ) . replace ( '-' , " _ " ) ) ;
cmd . env ( runner_env_name , & miri_path ) ;
// Run cargo.
if verbose {
cmd . env ( " MIRI_VERBOSE " , " " ) ; // this makes `inside_cargo_rustc` verbose.
2020-09-09 01:58:29 -05:00
eprintln! ( " [cargo-miri miri] {:?} " , cmd ) ;
2017-01-24 06:28:36 -06:00
}
2020-09-07 14:12:51 -05:00
exec ( cmd )
2017-01-24 06:28:36 -06:00
}
2020-09-09 01:58:29 -05:00
fn phase_cargo_rustc ( args : env ::Args ) {
2020-05-09 04:45:43 -05:00
/// Determines if we are being invoked (as rustc) to build a crate for
/// the "target" architecture, in contrast to the "host" architecture.
/// Host crates are for build scripts and proc macros and still need to
/// be built like normal; target crates need to be built for or interpreted
/// by Miri.
2020-02-24 07:17:19 -06:00
///
2020-05-09 04:53:24 -05:00
/// Currently, we detect this by checking for "--target=", which is
2020-05-09 04:45:43 -05:00
/// never set for host crates. This matches what rustc bootstrap does,
2020-05-09 04:54:35 -05:00
/// which hopefully makes it "reliable enough". This relies on us always
/// invoking cargo itself with `--target`, which `in_cargo_miri` ensures.
2020-02-24 07:17:19 -06:00
fn is_target_crate ( ) -> bool {
2020-05-09 04:45:43 -05:00
get_arg_flag_value ( " --target " ) . is_some ( )
2020-02-24 07:17:19 -06:00
}
2019-02-07 06:00:27 -06:00
2020-02-24 07:17:19 -06:00
/// Returns whether or not Cargo invoked the wrapper (this binary) to compile
2020-05-09 04:45:43 -05:00
/// the final, binary crate (either a test for 'cargo test', or a binary for 'cargo run')
2020-02-24 07:17:19 -06:00
/// Cargo does not give us this information directly, so we need to check
/// various command-line flags.
fn is_runnable_crate ( ) -> bool {
let is_bin = get_arg_flag_value ( " --crate-type " ) . as_deref ( ) = = Some ( " bin " ) ;
let is_test = has_arg_flag ( " --test " ) ;
2020-09-06 12:28:58 -05:00
let print = get_arg_flag_value ( " --print " ) . is_some ( ) ;
( is_bin | | is_test ) & & ! print
2020-02-24 07:17:19 -06:00
}
2020-01-01 03:12:27 -06:00
2020-09-08 13:08:11 -05:00
fn out_filename ( prefix : & str , suffix : & str ) -> PathBuf {
let mut path = PathBuf ::from ( get_arg_flag_value ( " --out-dir " ) . unwrap ( ) ) ;
path . push ( format! (
" {}{}{}{} " ,
prefix ,
get_arg_flag_value ( " --crate-name " ) . unwrap ( ) ,
// This is technically a `-C` flag but the prefix seems unique enough...
// (and cargo passes this before the filename so it should be unique)
get_arg_flag_value ( " extra-filename " ) . unwrap_or ( String ::new ( ) ) ,
suffix ,
) ) ;
path
}
2020-05-09 04:58:18 -05:00
let verbose = std ::env ::var_os ( " MIRI_VERBOSE " ) . is_some ( ) ;
2020-02-24 07:17:19 -06:00
let target_crate = is_target_crate ( ) ;
2020-09-06 12:28:58 -05:00
if target_crate & & is_runnable_crate ( ) {
// This is the binary or test crate that we want to interpret under Miri.
// But we cannot run it here, as cargo invoked us as a compiler -- our stdin and stdout are not
// like we want them.
// Instead of compiling, we write JSON into the output file with all the relevant command-line flags
2020-09-08 04:08:46 -05:00
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
2020-09-07 14:12:51 -05:00
let info = CrateRunInfo ::collect ( args ) ;
2020-09-08 13:08:11 -05:00
let filename = out_filename ( " " , " " ) ;
2020-09-09 01:58:29 -05:00
if verbose {
2020-09-08 13:18:08 -05:00
eprintln! ( " [cargo-miri rustc] writing run info to ` {} ` " , filename . display ( ) ) ;
2020-09-09 01:58:29 -05:00
}
2020-09-07 13:19:45 -05:00
2020-09-08 18:00:09 -05:00
info . store ( & filename ) ;
// For Windows, do the same thing again with `.exe` appended to the filename.
// (Need to do this here as cargo moves that "binary" to a different place before running it.)
info . store ( & out_filename ( " " , " .exe " ) ) ;
2020-09-06 12:28:58 -05:00
return ;
}
2020-05-10 11:41:03 -05:00
let mut cmd = miri ( ) ;
2020-09-08 13:08:11 -05:00
let mut emit_link_hack = false ;
// Arguments are treated very differently depending on whether this crate is
// for interpretation by Miri, or for use by a build script / proc macro.
if target_crate {
2020-09-08 13:18:08 -05:00
// Forward arguments, but remove "link" from "--emit" to make this a check-only build.
2020-09-08 13:08:11 -05:00
let emit_flag = " --emit " ;
for arg in args {
if arg . starts_with ( emit_flag ) {
// Patch this argument. First, extract its value.
let val = & arg [ emit_flag . len ( ) .. ] ;
assert! ( val . starts_with ( " = " ) , " `cargo` should pass `--emit=X` as one argument " ) ;
let val = & val [ 1 .. ] ;
let mut val : Vec < _ > = val . split ( ',' ) . collect ( ) ;
// Now make sure "link" is not in there, but "metadata" is.
if let Some ( i ) = val . iter ( ) . position ( | & s | s = = " link " ) {
emit_link_hack = true ;
val . remove ( i ) ;
if ! val . iter ( ) . any ( | & s | s = = " metadata " ) {
val . push ( " metadata " ) ;
}
2020-09-09 01:58:29 -05:00
}
2020-09-08 13:08:11 -05:00
cmd . arg ( format! ( " {} = {} " , emit_flag , val . join ( " , " ) ) ) ;
} else {
cmd . arg ( arg ) ;
2020-09-09 01:58:29 -05:00
}
}
2020-05-10 11:41:03 -05:00
2020-09-08 13:08:11 -05:00
// Use our custom sysroot.
2020-03-01 04:54:19 -06:00
let sysroot =
2020-05-10 11:41:03 -05:00
env ::var_os ( " MIRI_SYSROOT " ) . expect ( " The wrapper should have set MIRI_SYSROOT " ) ;
cmd . arg ( " --sysroot " ) ;
cmd . arg ( sysroot ) ;
2020-09-08 13:08:11 -05:00
} else {
// For host crates, just forward everything.
cmd . args ( args ) ;
2020-02-24 07:17:19 -06:00
}
2020-01-01 03:12:27 -06:00
2020-09-06 12:28:58 -05:00
// We want to compile, not interpret. We still use Miri to make sure the compiler version etc
// are the exact same as what is used for interpretation.
cmd . env ( " MIRI_BE_RUSTC " , " 1 " ) ;
2019-02-07 06:00:27 -06:00
2020-02-24 07:17:19 -06:00
// Run it.
if verbose {
2020-09-09 01:58:29 -05:00
eprintln! ( " [cargo-miri rustc] {:?} " , cmd ) ;
2019-02-07 06:00:27 -06:00
}
2020-09-08 13:08:11 -05:00
exec ( cmd ) ;
// Create a stub .rlib file if "link" was requested by cargo.
if emit_link_hack {
2020-09-08 13:18:08 -05:00
// Some platforms prepend "lib", some do not... let's just create both files.
2020-09-08 13:08:11 -05:00
let filename = out_filename ( " lib " , " .rlib " ) ;
File ::create ( filename ) . expect ( " Failed to create rlib file " ) ;
2020-09-08 13:18:08 -05:00
let filename = out_filename ( " " , " .rlib " ) ;
File ::create ( filename ) . expect ( " Failed to create rlib file " ) ;
2020-09-08 13:08:11 -05:00
}
2017-01-24 06:28:36 -06:00
}
2020-05-08 02:55:28 -05:00
2020-09-09 01:58:29 -05:00
fn phase_cargo_runner ( binary : & str , binary_args : env ::Args ) {
2020-09-07 14:12:51 -05:00
let verbose = std ::env ::var_os ( " MIRI_VERBOSE " ) . is_some ( ) ;
2020-09-07 13:19:45 -05:00
2020-09-08 18:00:09 -05:00
let file = File ::open ( & binary )
2020-09-09 01:58:29 -05:00
. unwrap_or_else ( | _ | show_error ( format! ( " File {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri` " , binary ) ) ) ;
2020-09-07 13:19:45 -05:00
let file = BufReader ::new ( file ) ;
let info : CrateRunInfo = serde_json ::from_reader ( file )
. unwrap_or_else ( | _ | show_error ( format! ( " File {:?} does not contain valid JSON " , binary ) ) ) ;
2020-09-07 14:12:51 -05:00
2020-09-09 01:51:41 -05:00
// Set missing env vars.
for ( name , val ) in info . env {
if env ::var_os ( & name ) . is_none ( ) {
env ::set_var ( name , val ) ;
}
}
2020-09-07 14:12:51 -05:00
let mut cmd = miri ( ) ;
2020-09-09 02:12:26 -05:00
// Forward rustc arguments.
// We need to patch "--extern" filenames because we forced a check-only
// build without cargo knowing about that: replace `.rlib` suffix by
// `.rmeta`.
// We also need to remove `--error-format` as cargo specifies that to be JSON,
// but when we run here, cargo does not interpret the JSON any more. `--json`
// then also nees to be dropped.
2020-09-09 01:58:29 -05:00
let mut args = info . args . into_iter ( ) ;
let extern_flag = " --extern " ;
2020-09-09 02:12:26 -05:00
let error_format_flag = " --error-format " ;
let json_flag = " --json " ;
2020-09-09 01:58:29 -05:00
while let Some ( arg ) = args . next ( ) {
if arg = = extern_flag {
2020-09-09 02:12:26 -05:00
// `--extern` is always passed as a separate argument by cargo.
2020-09-09 01:58:29 -05:00
let next_arg = args . next ( ) . expect ( " `--extern` should be followed by a filename " ) ;
let next_arg = next_arg . strip_suffix ( " .rlib " ) . expect ( " all extern filenames should end in `.rlib` " ) ;
cmd . arg ( extern_flag ) ;
cmd . arg ( format! ( " {} .rmeta " , next_arg ) ) ;
2020-09-09 02:12:26 -05:00
} else if arg . starts_with ( error_format_flag ) {
let suffix = & arg [ error_format_flag . len ( ) .. ] ;
assert! ( suffix . starts_with ( '=' ) ) ;
// Drop this argument.
} else if arg . starts_with ( json_flag ) {
let suffix = & arg [ json_flag . len ( ) .. ] ;
assert! ( suffix . starts_with ( '=' ) ) ;
// Drop this argument.
2020-09-09 01:58:29 -05:00
} else {
cmd . arg ( arg ) ;
}
}
// Set sysroot.
2020-09-07 14:12:51 -05:00
let sysroot =
env ::var_os ( " MIRI_SYSROOT " ) . expect ( " The wrapper should have set MIRI_SYSROOT " ) ;
cmd . arg ( " --sysroot " ) ;
cmd . arg ( sysroot ) ;
2020-09-09 01:58:29 -05:00
// Respect `MIRIFLAGS`.
if let Ok ( a ) = env ::var ( " MIRIFLAGS " ) {
// This code is taken from `RUSTFLAGS` handling in cargo.
let args = a
2020-09-08 18:00:09 -05:00
. split ( ' ' )
. map ( str ::trim )
. filter ( | s | ! s . is_empty ( ) )
. map ( str ::to_string ) ;
2020-09-09 01:58:29 -05:00
cmd . args ( args ) ;
}
2020-09-07 14:12:51 -05:00
// Then pass binary arguments.
cmd . arg ( " -- " ) ;
2020-09-09 01:58:29 -05:00
cmd . args ( binary_args ) ;
2020-09-07 14:12:51 -05:00
// Run it.
if verbose {
2020-09-09 01:58:29 -05:00
eprintln! ( " [cargo-miri runner] {:?} " , cmd ) ;
2020-09-07 14:12:51 -05:00
}
exec ( cmd )
2020-09-06 12:28:58 -05:00
}
2020-05-08 02:55:28 -05:00
fn main ( ) {
2020-09-09 01:58:29 -05:00
// Rustc does not support non-UTF-8 arguments so we make no attempt either.
// (We do support non-UTF-8 environment variables though.)
let mut args = std ::env ::args ( ) ;
2020-09-06 12:28:58 -05:00
// Skip binary name.
args . next ( ) . unwrap ( ) ;
// Dispatch to `cargo-miri` phase. There are three phases:
// - When we are called via `cargo miri`, we run as the frontend and invoke the underlying
// cargo. We set RUSTC_WRAPPER and CARGO_TARGET_RUNNER to ourselves.
// - When we are executed due to RUSTC_WRAPPER, we build crates or store the flags of
// binary crates for later interpretation.
// - When we are executed due to CARGO_TARGET_RUNNER, we start interpretation based on the
// flags that were stored earlier.
2020-09-09 01:58:29 -05:00
match args . next ( ) . as_deref ( ) {
Some ( " miri " ) = > phase_cargo_miri ( args ) ,
Some ( " rustc " ) = > phase_cargo_rustc ( args ) ,
Some ( binary ) = > phase_cargo_runner ( binary , args ) ,
_ = > show_error ( format! ( " `cargo-miri` called without first argument; please only invoke this binary through `cargo miri` " ) ) ,
2020-05-08 02:55:28 -05:00
}
}