2020-04-23 10:46:49 -05:00
use std ::env ;
2020-05-21 04:32:12 -05:00
use std ::ffi ::OsString ;
2021-05-16 04:10:27 -05:00
use std ::fmt ::Write as _ ;
2019-02-15 19:29:38 -06:00
use std ::fs ::{ self , File } ;
2021-01-12 15:16:48 -06:00
use std ::io ::{ self , BufRead , BufReader , BufWriter , Read , Write } ;
2021-05-16 04:10:27 -05:00
use std ::iter ::TakeWhile ;
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
2020-09-12 05:41:23 -05:00
const CARGO_MIRI_HELP : & str = r #" Runs binary crates and tests in Miri
2017-01-24 06:28:36 -06:00
Usage :
2020-09-12 05:41:23 -05:00
cargo miri [ subcommand ] [ < cargo options > .. . ] [ - - ] [ < program / test suite options > .. . ]
2018-11-25 09:30:11 -06:00
Subcommands :
2020-09-12 05:41:23 -05:00
run Run binaries
2018-11-25 09:30:11 -06:00
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
2020-09-12 05:41:23 -05:00
The cargo options are exactly the same as for ` cargo run ` and ` cargo test ` , respectively .
2020-05-17 03:08:45 -05:00
Examples :
2020-09-12 05:41:23 -05:00
cargo miri run
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 ,
}
2021-01-18 09:50:10 -06:00
/// The information to run a crate with the given environment.
2021-04-05 06:18:59 -05:00
#[ derive(Serialize, Deserialize) ]
2021-01-18 09:50:10 -06:00
struct CrateRunEnv {
2020-09-07 13:19:45 -05:00
/// 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 ) > ,
2020-09-12 06:52:05 -05:00
/// The current working directory.
current_dir : OsString ,
2021-01-12 15:16:48 -06:00
/// The contents passed via standard input.
stdin : Vec < u8 > ,
2020-09-07 14:12:51 -05:00
}
2021-04-05 05:26:27 -05:00
impl CrateRunEnv {
2020-09-07 14:12:51 -05:00
/// Gather all the information we need.
2021-04-05 06:18:59 -05:00
fn collect ( args : env ::Args , capture_stdin : bool ) -> Self {
2020-09-07 14:12:51 -05:00
let args = args . collect ( ) ;
let env = env ::vars_os ( ) . collect ( ) ;
2020-09-12 06:52:05 -05:00
let current_dir = env ::current_dir ( ) . unwrap ( ) . into_os_string ( ) ;
2021-01-12 15:16:48 -06:00
let mut stdin = Vec ::new ( ) ;
2021-04-05 06:18:59 -05:00
if capture_stdin {
2021-01-12 15:16:48 -06:00
std ::io ::stdin ( ) . lock ( ) . read_to_end ( & mut stdin ) . expect ( " cannot read stdin " ) ;
}
2021-04-05 05:26:27 -05:00
CrateRunEnv { args , env , current_dir , stdin }
2020-09-07 14:12:51 -05:00
}
2021-04-05 05:26:27 -05:00
}
2020-09-08 18:00:09 -05:00
2021-04-05 05:26:27 -05:00
/// The information Miri needs to run a crate. Stored as JSON when the crate is "compiled".
#[ derive(Serialize, Deserialize) ]
enum CrateRunInfo {
/// Run it with the given environment.
RunWith ( CrateRunEnv ) ,
/// Skip it as Miri does not support interpreting such kind of crates.
SkipProcMacroTest ,
}
impl CrateRunInfo {
2020-09-08 18:00:09 -05:00
fn store ( & self , filename : & Path ) {
let file = File ::create ( filename )
2020-09-12 07:02:08 -05:00
. unwrap_or_else ( | _ | show_error ( format! ( " cannot create ` {} ` " , filename . display ( ) ) ) ) ;
2020-09-08 18:00:09 -05:00
let file = BufWriter ::new ( file ) ;
serde_json ::ser ::to_writer ( file , self )
2020-09-12 07:02:08 -05:00
. unwrap_or_else ( | _ | show_error ( format! ( " cannot write to ` {} ` " , filename . display ( ) ) ) ) ;
2020-09-08 18:00:09 -05:00
}
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 ( ) {
2021-05-15 07:17:30 -05:00
let mut version = format! ( " miri {} " , env! ( " CARGO_PKG_VERSION " ) ) ;
// Only use `option_env` on vergen variables to ensure the build succeeds
// when vergen failed to find the git info.
if let Some ( sha ) = option_env! ( " VERGEN_GIT_SHA_SHORT " ) {
2021-05-16 04:10:27 -05:00
write! ( & mut version , " ({} {}) " , sha , option_env! ( " VERGEN_GIT_COMMIT_DATE " ) . unwrap ( ) )
. unwrap ( ) ;
2021-05-15 07:17:30 -05:00
}
println! ( " {} " , version ) ;
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 )
}
2021-01-18 09:50:10 -06:00
/// Yields all values of command line flag `name`.
2021-01-18 04:17:25 -06:00
struct ArgFlagValueIter < ' a > {
args : TakeWhile < env ::Args , fn ( & String ) -> bool > ,
name : & ' a str ,
}
impl < ' a > ArgFlagValueIter < ' a > {
fn new ( name : & ' a str ) -> Self {
Self {
// Stop searching at `--`.
args : env ::args ( ) . take_while ( | val | val ! = " -- " ) ,
name ,
2019-02-07 06:00:27 -06:00
}
2021-01-18 04:17:25 -06:00
}
}
impl Iterator for ArgFlagValueIter < '_ > {
type Item = String ;
fn next ( & mut self ) -> Option < Self ::Item > {
loop {
let arg = self . args . next ( ) ? ;
if ! arg . starts_with ( self . name ) {
continue ;
}
// Strip leading `name`.
let suffix = & arg [ self . name . len ( ) .. ] ;
if suffix . is_empty ( ) {
// This argument is exactly `name`; the next one is the value.
return self . args . next ( ) ;
} else if suffix . starts_with ( '=' ) {
// 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
}
}
2021-01-18 04:17:25 -06:00
/// Gets the value of a `--flag`.
fn get_arg_flag_value ( name : & str ) -> Option < String > {
ArgFlagValueIter ::new ( name ) . next ( )
}
2021-02-14 10:52:18 -06:00
fn forward_patched_extern_arg ( args : & mut impl Iterator < Item = String > , cmd : & mut Command ) {
cmd . arg ( " --extern " ) ; // always forward flag, but adjust filename:
let path = args . next ( ) . expect ( " `--extern` should be followed by a filename " ) ;
if let Some ( lib ) = path . strip_suffix ( " .rlib " ) {
// If this is an rlib, make it an rmeta.
cmd . arg ( format! ( " {} .rmeta " , lib ) ) ;
} else {
// Some other extern file (e.g. a `.so`). Forward unchanged.
cmd . arg ( path ) ;
}
}
2021-04-05 05:16:31 -05:00
fn forward_miri_sysroot ( cmd : & mut Command ) {
2021-05-16 04:10:27 -05:00
let sysroot = env ::var_os ( " MIRI_SYSROOT " ) . expect ( " the wrapper should have set MIRI_SYSROOT " ) ;
2021-04-05 05:16:31 -05:00
cmd . arg ( " --sysroot " ) ;
cmd . arg ( sysroot ) ;
}
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-12 07:09:43 -05:00
/// Execute the command. If it fails, fail this process with the same exit code.
2020-09-08 13:08:11 -05:00
/// 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
}
2021-01-12 15:16:48 -06:00
/// Execute the command and pipe `input` into its stdin.
/// If it fails, fail this process with the same exit code.
/// Otherwise, continue.
fn exec_with_pipe ( mut cmd : Command , input : & [ u8 ] ) {
cmd . stdin ( std ::process ::Stdio ::piped ( ) ) ;
let mut child = cmd . spawn ( ) . expect ( " failed to spawn process " ) ;
{
let stdin = child . stdin . as_mut ( ) . expect ( " failed to open stdin " ) ;
stdin . write_all ( input ) . expect ( " failed to write out test source " ) ;
}
let exit_status = child . wait ( ) . expect ( " failed to run command " ) ;
if exit_status . success ( ) . not ( ) {
std ::process ::exit ( exit_status . code ( ) . unwrap_or ( - 1 ) )
}
}
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 " = > { }
2020-09-12 07:02:08 -05:00
" n " | " no " = > show_error ( format! ( " aborting as per your request " ) ) ,
a = > show_error ( format! ( " invalid answer ` {} ` " , 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 ( ) {
2020-09-12 07:02:08 -05:00
show_error ( format! ( " failed to {} " , text ) ) ;
2019-09-13 03:39:36 -05:00
}
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.
2020-09-12 07:02:08 -05:00
show_error ( format! ( " xargo is too old; please upgrade to the latest version " ) )
2019-10-21 03:25:47 -05:00
}
2019-09-13 03:39:36 -05:00
let mut cmd = cargo ( ) ;
2020-10-23 13:48:34 -05:00
cmd . args ( & [ " install " , " xargo " ] ) ;
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 ( ) {
2020-09-12 07:02:08 -05:00
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
2020-09-18 06:10:18 -05:00
# We support unwinding , so enable that panic runtime .
2020-10-20 12:27:58 -05:00
features = [ " panic_unwind " , " backtrace " ]
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.
2020-09-18 06:10:18 -05:00
// We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
// for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).
2021-04-18 05:29:31 -05:00
// The `MIRI_CALLED_FROM_XARGO` will mean we dispatch to `phase_setup_rustc`.
2020-09-18 06:10:18 -05:00
let cargo_miri_path = std ::env ::current_exe ( ) . expect ( " current executable path invalid " ) ;
2020-05-09 05:24:30 -05:00
if env ::var_os ( " RUSTC_STAGE " ) . is_some ( ) {
2020-09-18 06:10:18 -05:00
command . env ( " RUSTC_REAL " , & cargo_miri_path ) ;
2020-05-09 05:24:30 -05:00
} else {
2020-09-18 06:10:18 -05:00
command . env ( " RUSTC " , & cargo_miri_path ) ;
2018-12-10 02:32:54 -06:00
}
2021-04-18 05:29:31 -05:00
command . env ( " MIRI_CALLED_FROM_XARGO " , " 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 " ) ;
2020-10-28 04:48:33 -05:00
// Disable debug assertions in the standard library -- Miri is already slow enough.
2020-11-07 08:56:25 -06:00
// But keep the overflow checks, they are cheap.
command . env ( " RUSTFLAGS " , " -Cdebug-assertions=off -Coverflow-checks=on " ) ;
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 ( ) {
2020-09-12 07:02:08 -05:00
show_error ( format! ( " failed to run xargo " ) ) ;
2018-11-25 10:09:49 -06:00
}
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.
2021-05-16 04:10:27 -05:00
_ = > show_error ( format! (
" `cargo miri` supports the following subcommands: `run`, `test`, and `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.
2020-09-16 12:54:52 -05:00
// We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
// requires some extra work to make the build check-only (see all the `--emit` hacks below).
// <https://github.com/rust-lang/miri/pull/1540#issuecomment-693553191> describes an alternative
// approach that uses `cargo check`, making that part easier but target and binary handling
// harder.
2020-09-18 06:10:18 -05:00
let cargo_miri_path = std ::env ::current_exe ( ) . expect ( " current executable path invalid " ) ;
2020-09-06 12:28:58 -05:00
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.
2021-01-18 09:50:10 -06:00
let host = version_info ( ) . host ;
let target = get_arg_flag_value ( " --target " ) ;
let target = if let Some ( ref target ) = target {
2020-09-06 12:28:58 -05:00
target
} else {
// No target given. Pick default and tell cargo about it.
cmd . arg ( " --target " ) ;
cmd . arg ( & host ) ;
2021-01-18 09:50:10 -06:00
& host
2020-09-06 12:28:58 -05:00
} ;
2020-01-01 03:12:27 -06:00
2021-04-10 07:07:46 -05:00
// Forward all further arguments to cargo.
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 ( ) {
2021-05-16 04:10:27 -05:00
println! (
" WARNING: Ignoring `RUSTC_WRAPPER` environment variable, Miri does not support wrapping. "
) ;
2020-09-06 12:28:58 -05:00
}
2020-09-18 06:10:18 -05:00
cmd . env ( " RUSTC_WRAPPER " , & cargo_miri_path ) ;
2019-02-07 06:00:27 -06:00
2021-05-16 04:10:27 -05:00
let runner_env_name =
| triple : & str | format! ( " CARGO_TARGET_ {} _RUNNER " , triple . to_uppercase ( ) . replace ( '-' , " _ " ) ) ;
2021-01-18 09:50:10 -06:00
let host_runner_env_name = runner_env_name ( & host ) ;
let target_runner_env_name = runner_env_name ( target ) ;
// Set the target runner to us, so we can interpret the binaries.
cmd . env ( & target_runner_env_name , & cargo_miri_path ) ;
// Unit tests of `proc-macro` crates are run on the host, so we set the host runner to
// us in order to skip them.
cmd . env ( & host_runner_env_name , & cargo_miri_path ) ;
2020-09-06 12:28:58 -05:00
2021-04-05 04:55:53 -05:00
// Set rustdoc to us as well, so we can run doctests.
2020-09-18 06:10:18 -05:00
cmd . env ( " RUSTDOC " , & cargo_miri_path ) ;
2020-09-11 08:05:05 -05:00
2020-09-06 12:28:58 -05:00
// Run cargo.
if verbose {
2020-09-18 06:10:18 -05:00
eprintln! ( " [cargo-miri miri] RUSTC_WRAPPER= {:?} " , cargo_miri_path ) ;
2021-01-18 09:50:10 -06:00
eprintln! ( " [cargo-miri miri] {} = {:?} " , target_runner_env_name , cargo_miri_path ) ;
if * target ! = host {
eprintln! ( " [cargo-miri miri] {} = {:?} " , host_runner_env_name , cargo_miri_path ) ;
}
2020-09-18 06:10:18 -05:00
eprintln! ( " [cargo-miri miri] RUSTDOC= {:?} " , cargo_miri_path ) ;
2020-09-09 01:58:29 -05:00
eprintln! ( " [cargo-miri miri] {:?} " , cmd ) ;
2020-09-18 06:10:18 -05:00
cmd . env ( " MIRI_VERBOSE " , " " ) ; // This makes the other phases verbose.
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
}
2021-04-18 05:29:31 -05:00
#[ derive(Debug, Copy, Clone, PartialEq) ]
enum RustcPhase {
/// `rustc` called via `xargo` for sysroot build.
Setup ,
/// `rustc` called by `cargo` for regular build.
Build ,
/// `rustc` called by `rustdoc` for doctest.
Rustdoc ,
}
fn phase_rustc ( mut args : env ::Args , phase : RustcPhase ) {
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 {
2020-09-12 05:52:14 -05:00
let is_bin = get_arg_flag_value ( " --crate-type " ) . as_deref ( ) . unwrap_or ( " bin " ) = = " bin " ;
2020-02-24 07:17:19 -06:00
let is_test = has_arg_flag ( " --test " ) ;
2020-10-07 12:42:33 -05:00
is_bin | | is_test
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 {
2021-01-12 15:16:48 -06:00
if let Some ( out_dir ) = get_arg_flag_value ( " --out-dir " ) {
let mut path = PathBuf ::from ( out_dir ) ;
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
} else {
let out_file = get_arg_flag_value ( " -o " ) . unwrap ( ) ;
PathBuf ::from ( out_file )
}
2020-09-08 13:08:11 -05:00
}
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 ( ) ;
2021-04-18 05:29:31 -05:00
let print = get_arg_flag_value ( " --print " ) . is_some ( ) | | has_arg_flag ( " -vV " ) ; // whether this is cargo/xargo invoking rustc to get some infos
2020-10-07 12:42:33 -05:00
2021-04-05 05:26:27 -05:00
let store_json = | info : CrateRunInfo | {
2021-02-27 14:52:27 -06:00
// Create a stub .d file to stop Cargo from "rebuilding" the crate:
// https://github.com/rust-lang/miri/issues/1724#issuecomment-787115693
// As we store a JSON file instead of building the crate here, an empty file is fine.
let dep_info_name = out_filename ( " " , " .d " ) ;
if verbose {
2021-02-28 01:57:49 -06:00
eprintln! ( " [cargo-miri rustc] writing stub dep-info to ` {} ` " , dep_info_name . display ( ) ) ;
2021-02-27 14:52:27 -06:00
}
File ::create ( dep_info_name ) . expect ( " failed to create fake .d file " ) ;
2021-02-28 01:57:49 -06:00
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-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 " ) ) ;
2021-01-18 09:50:10 -06:00
} ;
2020-09-08 18:00:09 -05:00
2021-01-18 09:50:10 -06:00
let runnable_crate = ! print & & is_runnable_crate ( ) ;
2020-09-08 18:00:09 -05:00
2021-01-18 09:50:10 -06:00
if runnable_crate & & target_crate {
2021-05-16 04:10:27 -05:00
assert! (
phase ! = RustcPhase ::Setup ,
" there should be no interpretation during sysroot build "
) ;
2021-04-18 05:29:31 -05:00
let inside_rustdoc = phase = = RustcPhase ::Rustdoc ;
2021-01-18 09:50:10 -06:00
// 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
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
2021-04-05 06:18:59 -05:00
let env = CrateRunEnv ::collect ( args , inside_rustdoc ) ;
2021-01-12 15:16:48 -06:00
// Rustdoc expects us to exit with an error code if the test is marked as `compile_fail`,
// just creating the JSON file is not enough: we need to detect syntax errors,
// so we need to run Miri with `MIRI_BE_RUSTC` for a check-only build.
2021-04-05 06:18:59 -05:00
if inside_rustdoc {
2021-01-12 15:16:48 -06:00
let mut cmd = miri ( ) ;
2021-04-05 05:26:27 -05:00
// Ensure --emit argument for a check-only build is present.
2021-04-05 05:46:36 -05:00
// We cannot use the usual helpers since we need to check specifically in `env.args`.
2021-01-12 15:16:48 -06:00
if let Some ( i ) = env . args . iter ( ) . position ( | arg | arg . starts_with ( " --emit= " ) ) {
2021-04-05 05:26:27 -05:00
// For `no_run` tests, rustdoc passes a `--emit` flag; make sure it has the right shape.
2021-01-12 15:16:48 -06:00
assert_eq! ( env . args [ i ] , " --emit=metadata " ) ;
} else {
2021-04-05 05:26:27 -05:00
// For all other kinds of tests, we can just add our flag.
cmd . arg ( " --emit=metadata " ) ;
2021-01-12 15:16:48 -06:00
}
2021-04-05 05:26:27 -05:00
cmd . args ( & env . args ) ;
2021-04-07 07:46:20 -05:00
cmd . env ( " MIRI_BE_RUSTC " , " target " ) ;
2021-01-12 15:16:48 -06:00
if verbose {
2021-05-16 04:10:27 -05:00
eprintln! (
" [cargo-miri rustc] captured input: \n {} " ,
std ::str ::from_utf8 ( & env . stdin ) . unwrap ( )
) ;
2021-01-12 15:16:48 -06:00
eprintln! ( " [cargo-miri rustc] {:?} " , cmd ) ;
}
2021-02-14 04:57:03 -06:00
2021-01-12 15:16:48 -06:00
exec_with_pipe ( cmd , & env . stdin ) ;
}
2021-04-05 05:26:27 -05:00
store_json ( CrateRunInfo ::RunWith ( env ) ) ;
2021-01-18 09:50:10 -06:00
return ;
}
if runnable_crate & & ArgFlagValueIter ::new ( " --extern " ) . any ( | krate | krate = = " proc_macro " ) {
// This is a "runnable" `proc-macro` crate (unit tests). We do not support
// interpreting that under Miri now, so we write a JSON file to (display a
// helpful message and) skip it in the runner phase.
2021-04-05 05:26:27 -05:00
store_json ( CrateRunInfo ::SkipProcMacroTest ) ;
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.
2020-10-07 12:42:33 -05:00
if ! print & & 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 " ;
2021-02-14 04:12:33 -06:00
while let Some ( arg ) = args . next ( ) {
2020-09-08 13:08:11 -05:00
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 ( " , " ) ) ) ;
2021-02-14 04:12:33 -06:00
} else if arg = = " --extern " {
// Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files:
// https://github.com/rust-lang/miri/issues/1705
forward_patched_extern_arg ( & mut args , & mut cmd ) ;
2020-09-08 13:08:11 -05:00
} else {
cmd . arg ( arg ) ;
2020-09-09 01:58:29 -05:00
}
}
2020-05-10 11:41:03 -05:00
2021-04-18 05:29:31 -05:00
// Use our custom sysroot (but not if that is what we are currently building).
if phase ! = RustcPhase ::Setup {
forward_miri_sysroot ( & mut cmd ) ;
}
// During setup, patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does).
2021-05-16 04:10:27 -05:00
if phase = = RustcPhase ::Setup
& & get_arg_flag_value ( " --crate-name " ) . as_deref ( ) = = Some ( " panic_abort " )
{
2021-04-18 05:29:31 -05:00
cmd . arg ( " -C " ) . arg ( " panic=abort " ) ;
}
2020-09-08 13:08:11 -05:00
} else {
2020-10-07 12:42:33 -05:00
// For host crates or when we are printing, just forward everything.
2020-09-08 13:08:11 -05:00
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.
2021-04-07 07:46:20 -05:00
// MIRI_DEFAULT_ARGS should not be used to build host crates, hence setting "target" or "host"
// as the value here to help Miri differentiate them.
cmd . env ( " MIRI_BE_RUSTC " , if target_crate { " target " } else { " host " } ) ;
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.
2021-02-27 05:32:06 -06:00
// This is necessary to prevent cargo from doing rebuilds all the time.
2020-09-08 13:08:11 -05:00
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.
2021-02-27 05:32:06 -06:00
File ::create ( out_filename ( " lib " , " .rlib " ) ) . expect ( " failed to create fake .rlib file " ) ;
File ::create ( out_filename ( " " , " .rlib " ) ) . expect ( " failed to create fake .rlib file " ) ;
// Just in case this is a cdylib or staticlib, also create those fake files.
File ::create ( out_filename ( " lib " , " .so " ) ) . expect ( " failed to create fake .so file " ) ;
File ::create ( out_filename ( " lib " , " .a " ) ) . expect ( " failed to create fake .a file " ) ;
File ::create ( out_filename ( " lib " , " .dylib " ) ) . expect ( " failed to create fake .dylib file " ) ;
File ::create ( out_filename ( " " , " .dll " ) ) . expect ( " failed to create fake .dll file " ) ;
File ::create ( out_filename ( " " , " .lib " ) ) . expect ( " failed to create fake .lib 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
2021-04-18 05:29:31 -05:00
#[ derive(Debug, Copy, Clone, PartialEq) ]
enum RunnerPhase {
/// `cargo` is running a binary
Cargo ,
/// `rustdoc` is running a binary
Rustdoc ,
}
fn phase_runner ( binary : & Path , binary_args : env ::Args , phase : RunnerPhase ) {
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-12 07:02:08 -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 ) ;
2021-01-18 09:50:10 -06:00
2021-05-16 04:10:27 -05:00
let info = serde_json ::from_reader ( file ) . unwrap_or_else ( | _ | {
show_error ( format! (
" file {:?} contains outdated or invalid JSON; try `cargo clean` " ,
binary
) )
} ) ;
2021-01-18 09:50:10 -06:00
let info = match info {
CrateRunInfo ::RunWith ( info ) = > info ,
CrateRunInfo ::SkipProcMacroTest = > {
2021-05-16 04:10:27 -05:00
eprintln! (
" Running unit tests of `proc-macro` crates is not currently supported by Miri. "
) ;
2021-01-18 09:50:10 -06:00
return ;
}
} ;
2020-09-07 14:12:51 -05:00
2021-01-23 09:51:29 -06:00
let mut cmd = miri ( ) ;
// Set missing env vars. We prefer build-time env vars over run-time ones; see
// <https://github.com/rust-lang/miri/issues/1661> for the kind of issue that fixes.
2020-09-09 01:51:41 -05:00
for ( name , val ) in info . env {
2021-01-23 09:51:29 -06:00
if verbose {
if let Some ( old_val ) = env ::var_os ( & name ) {
if old_val ! = val {
2021-05-16 04:10:27 -05:00
eprintln! (
" [cargo-miri runner] Overwriting run-time env var {:?}={:?} with build-time value {:?} " ,
name , old_val , val
) ;
2021-01-23 09:51:29 -06:00
}
}
2020-09-09 01:51:41 -05:00
}
2021-01-23 09:51:29 -06:00
cmd . env ( name , val ) ;
2020-09-09 01:51:41 -05:00
}
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 ( ) ;
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 ( ) {
2021-02-14 10:52:18 -06:00
if arg = = " --extern " {
forward_patched_extern_arg ( & mut args , & mut cmd ) ;
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 ) ;
}
}
2021-04-18 05:29:31 -05:00
// Set sysroot (if we are inside rustdoc, we already did that in `phase_cargo_rustdoc`).
if phase ! = RunnerPhase ::Rustdoc {
2021-04-05 05:16:31 -05:00
forward_miri_sysroot ( & mut cmd ) ;
2021-02-14 04:57:03 -06:00
}
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.
2021-05-16 04:10:27 -05:00
let args = a . 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
2020-09-12 06:52:05 -05:00
// Make sure we use the build-time working directory for interpreting Miri/rustc arguments.
// But then we need to switch to the run-time one, which we instruct Miri do do by setting `MIRI_CWD`.
cmd . current_dir ( info . current_dir ) ;
cmd . env ( " MIRI_CWD " , env ::current_dir ( ) . unwrap ( ) ) ;
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
}
2021-01-12 15:16:48 -06:00
2021-04-18 05:29:31 -05:00
match phase {
2021-05-16 04:10:27 -05:00
RunnerPhase ::Rustdoc = > exec_with_pipe ( cmd , & info . stdin ) ,
RunnerPhase ::Cargo = > exec ( cmd ) ,
2021-01-12 15:16:48 -06:00
}
}
2021-04-18 05:29:31 -05:00
fn phase_rustdoc ( fst_arg : & str , mut args : env ::Args ) {
2021-01-12 15:16:48 -06:00
let verbose = std ::env ::var_os ( " MIRI_VERBOSE " ) . is_some ( ) ;
// phase_cargo_miri sets the RUSTDOC env var to ourselves, so we can't use that here;
// just default to a straight-forward invocation for now:
2021-04-05 05:46:36 -05:00
let mut cmd = Command ::new ( " rustdoc " ) ;
2021-01-12 15:16:48 -06:00
// Because of the way the main function is structured, we have to take the first argument spearately
// from the rest; to simplify the following argument patching loop, we'll just skip that one.
// This is fine for now, because cargo will never pass --extern arguments in the first position,
// but we should defensively assert that this will work.
let extern_flag = " --extern " ;
assert! ( fst_arg ! = extern_flag ) ;
cmd . arg ( fst_arg ) ;
2021-02-14 04:57:03 -06:00
2021-01-12 15:16:48 -06:00
let runtool_flag = " --runtool " ;
2021-04-05 05:46:36 -05:00
// `crossmode` records if *any* argument matches `runtool_flag`; here we check the first one.
2021-01-12 15:16:48 -06:00
let mut crossmode = fst_arg = = runtool_flag ;
while let Some ( arg ) = args . next ( ) {
if arg = = extern_flag {
// Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files.
forward_patched_extern_arg ( & mut args , & mut cmd ) ;
} else if arg = = runtool_flag {
// An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support.
// Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag;
// otherwise, we won't be called as rustdoc at all.
crossmode = true ;
break ;
} else {
cmd . arg ( arg ) ;
}
}
if crossmode {
2021-05-27 06:48:07 -05:00
show_error ( format! ( " cross-interpreting doctests is not currently supported by Miri. " ) ) ;
2021-01-12 15:16:48 -06:00
}
2021-05-27 06:44:48 -05:00
// Doctests of `proc-macro` crates (and their dependencies) are always built for the host,
2021-05-26 17:40:47 -05:00
// so we are not able to run them in Miri.
if ArgFlagValueIter ::new ( " --crate-type " ) . any ( | crate_type | crate_type = = " proc-macro " ) {
2021-05-27 05:34:38 -05:00
eprintln! ( " Running doctests of `proc-macro` crates is not currently supported by Miri. " ) ;
2021-05-26 17:40:47 -05:00
return ;
}
2021-05-27 06:48:07 -05:00
// For each doctest, rustdoc starts two child processes: first the test is compiled,
2021-01-12 15:16:48 -06:00
// then the produced executable is invoked. We want to reroute both of these to cargo-miri,
// such that the first time we'll enter phase_cargo_rustc, and phase_cargo_runner second.
2021-02-14 04:57:03 -06:00
//
2021-01-12 15:16:48 -06:00
// rustdoc invokes the test-builder by forwarding most of its own arguments, which makes
// it difficult to determine when phase_cargo_rustc should run instead of phase_cargo_rustdoc.
// Furthermore, the test code is passed via stdin, rather than a temporary file, so we need
// to let phase_cargo_rustc know to expect that. We'll use this environment variable as a flag:
cmd . env ( " MIRI_CALLED_FROM_RUSTDOC " , " 1 " ) ;
2021-02-14 04:57:03 -06:00
2021-01-12 15:16:48 -06:00
// The `--test-builder` and `--runtool` arguments are unstable rustdoc features,
// which are disabled by default. We first need to enable them explicitly:
cmd . arg ( " -Z " ) . arg ( " unstable-options " ) ;
2021-02-14 04:57:03 -06:00
2021-04-05 05:16:31 -05:00
// rustdoc needs to know the right sysroot.
forward_miri_sysroot ( & mut cmd ) ;
2021-02-14 04:57:03 -06:00
2021-04-05 05:16:31 -05:00
// Make rustdoc call us back.
2021-01-12 15:16:48 -06:00
let cargo_miri_path = std ::env ::current_exe ( ) . expect ( " current executable path invalid " ) ;
cmd . arg ( " --test-builder " ) . arg ( & cargo_miri_path ) ; // invoked by forwarding most arguments
cmd . arg ( " --runtool " ) . arg ( & cargo_miri_path ) ; // invoked with just a single path argument
2021-02-14 04:57:03 -06:00
2021-01-12 15:16:48 -06:00
if verbose {
eprintln! ( " [cargo-miri rustdoc] {:?} " , 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 ( ) ;
2020-09-18 06:10:18 -05:00
// Dispatch running as part of sysroot compilation.
2021-04-18 05:29:31 -05:00
if env ::var_os ( " MIRI_CALLED_FROM_XARGO " ) . is_some ( ) {
phase_rustc ( args , RustcPhase ::Setup ) ;
2020-09-18 06:10:18 -05:00
return ;
}
2021-04-05 05:46:36 -05:00
// The way rustdoc invokes rustc is indistuingishable from the way cargo invokes rustdoc by the
// arguments alone. `phase_cargo_rustdoc` sets this environment variable to let us disambiguate.
2021-04-18 05:29:31 -05:00
if env ::var_os ( " MIRI_CALLED_FROM_RUSTDOC " ) . is_some ( ) {
2021-01-12 15:16:48 -06:00
// ...however, we then also see this variable when rustdoc invokes us as the testrunner!
// The runner is invoked as `$runtool ($runtool-arg)* output_file`;
// since we don't specify any runtool-args, and rustdoc supplies multiple arguments to
// the test-builder unconditionally, we can just check the number of remaining arguments:
if args . len ( ) = = 1 {
let arg = args . next ( ) . unwrap ( ) ;
let binary = Path ::new ( & arg ) ;
if binary . exists ( ) {
2021-04-18 05:29:31 -05:00
phase_runner ( binary , args , RunnerPhase ::Rustdoc ) ;
2021-01-12 15:16:48 -06:00
} else {
2021-05-16 04:10:27 -05:00
show_error ( format! (
" `cargo-miri` called with non-existing path argument `{}` in rustdoc mode; please invoke this binary through `cargo miri` " ,
arg
) ) ;
2021-01-12 15:16:48 -06:00
}
} else {
2021-04-18 05:29:31 -05:00
phase_rustc ( args , RustcPhase ::Rustdoc ) ;
2021-01-12 15:16:48 -06:00
}
return ;
}
2020-09-06 12:28:58 -05:00
// 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-11 08:05:05 -05:00
// On top of that, we are also called as RUSTDOC, but that is just a stub currently.
2020-09-09 01:58:29 -05:00
match args . next ( ) . as_deref ( ) {
Some ( " miri " ) = > phase_cargo_miri ( args ) ,
2021-04-18 05:29:31 -05:00
Some ( " rustc " ) = > phase_rustc ( args , RustcPhase ::Build ) ,
2020-09-11 08:05:05 -05:00
Some ( arg ) = > {
2021-01-12 15:16:48 -06:00
// We have to distinguish the "runner" and "rustdoc" cases.
2020-09-11 08:05:05 -05:00
// As runner, the first argument is the binary (a file that should exist, with an absolute path);
2021-01-12 15:16:48 -06:00
// as rustdoc, the first argument is a flag (`--something`).
2020-09-11 08:05:05 -05:00
let binary = Path ::new ( arg ) ;
if binary . exists ( ) {
assert! ( ! arg . starts_with ( " -- " ) ) ; // not a flag
2021-04-18 05:29:31 -05:00
phase_runner ( binary , args , RunnerPhase ::Cargo ) ;
2020-09-11 08:05:05 -05:00
} else if arg . starts_with ( " -- " ) {
2021-04-18 05:29:31 -05:00
phase_rustdoc ( arg , args ) ;
2020-09-11 08:05:05 -05:00
} else {
2021-05-16 04:10:27 -05:00
show_error ( format! (
" `cargo-miri` called with unexpected first argument `{}`; please only invoke this binary through `cargo miri` " ,
arg
) ) ;
2020-09-11 08:05:05 -05:00
}
}
2021-05-16 04:10:27 -05:00
_ = > 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
}
}