blob: 0620f3aaf099305fa3cecb83be341987de9a0fb0 [file] [log] [blame] [edit]
#![allow(clippy::needless_question_mark)]
mod args;
mod commands;
mod util;
use std::ops::Range;
use anyhow::{Context, Result, anyhow, bail};
#[derive(Clone, Debug)]
pub enum Command {
/// Installs the miri driver and cargo-miri.
/// Sets up the rpath such that the installed binary should work in any
/// working directory. Note that the binaries are placed in the `miri` toolchain
/// sysroot, to prevent conflicts with other toolchains.
Install {
/// Flags that are passed through to `cargo install`.
flags: Vec<String>,
},
/// Just build miri.
Build {
/// Flags that are passed through to `cargo build`.
flags: Vec<String>,
},
/// Just check miri.
Check {
/// Flags that are passed through to `cargo check`.
flags: Vec<String>,
},
/// Build miri, set up a sysroot and then run the test suite.
Test {
bless: bool,
/// The cross-interpretation target.
/// If none then the host is the target.
target: Option<String>,
/// Flags that are passed through to the test harness.
flags: Vec<String>,
},
/// Build miri, set up a sysroot and then run the driver with the given <flags>.
/// (Also respects MIRIFLAGS environment variable.)
Run {
dep: bool,
verbose: bool,
many_seeds: Option<Range<u32>>,
target: Option<String>,
edition: Option<String>,
/// Flags that are passed through to `miri`.
flags: Vec<String>,
},
/// Build documentation
Doc {
/// Flags that are passed through to `cargo doc`.
flags: Vec<String>,
},
/// Format all sources and tests.
Fmt {
/// Flags that are passed through to `rustfmt`.
flags: Vec<String>,
},
/// Runs clippy on all sources.
Clippy {
/// Flags that are passed through to `cargo clippy`.
flags: Vec<String>,
},
/// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
Bench {
target: Option<String>,
/// List of benchmarks to run. By default all benchmarks are run.
benches: Vec<String>,
},
/// Update and activate the rustup toolchain 'miri' to the commit given in the
/// `rust-version` file.
/// `rustup-toolchain-install-master` must be installed for this to work. Any extra
/// flags are passed to `rustup-toolchain-install-master`.
Toolchain { flags: Vec<String> },
/// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
/// rustc commit. The fetched commit is stored in the `rust-version` file, so the
/// next `./miri toolchain` will install the rustc that just got pulled.
RustcPull { commit: Option<String> },
/// Push Miri changes back to the rustc repo. This will pull a copy of the rustc
/// history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
/// clone of the rustc repo.
RustcPush { github_user: String, branch: String },
}
const HELP: &str = r#" COMMANDS
./miri build <flags>:
Just build miri. <flags> are passed to `cargo build`.
./miri check <flags>:
Just check miri. <flags> are passed to `cargo check`.
./miri test [--bless] [--target <target>] <flags>:
Build miri, set up a sysroot and then run the test suite.
<flags> are passed to the test harness.
./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] <flags>:
Build miri, set up a sysroot and then run the driver with the given <flags>.
(Also respects MIRIFLAGS environment variable.)
If `--many-seeds` is present, Miri is run many times in parallel with different seeds.
The range defaults to `0..64`.
./miri fmt <flags>:
Format all sources and tests. <flags> are passed to `rustfmt`.
./miri clippy <flags>:
Runs clippy on all sources. <flags> are passed to `cargo clippy`.
./miri cargo <flags>:
Runs just `cargo <flags>` with the Miri-specific environment variables.
Mainly meant to be invoked by rust-analyzer.
./miri install <flags>:
Installs the miri driver and cargo-miri. <flags> are passed to `cargo
install`. Sets up the rpath such that the installed binary should work in any
working directory. Note that the binaries are placed in the `miri` toolchain
sysroot, to prevent conflicts with other toolchains.
./miri bench [--target <target>] <benches>:
Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
<benches> can explicitly list the benchmarks to run; by default, all of them are run.
./miri toolchain <flags>:
Update and activate the rustup toolchain 'miri' to the commit given in the
`rust-version` file.
`rustup-toolchain-install-master` must be installed for this to work. Any extra
flags are passed to `rustup-toolchain-install-master`.
./miri rustc-pull <commit>:
Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
rustc commit. The fetched commit is stored in the `rust-version` file, so the
next `./miri toolchain` will install the rustc that just got pulled.
./miri rustc-push <github user> [<branch>]:
Push Miri changes back to the rustc repo. This will pull a copy of the rustc
history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
clone of the rustc repo. The branch defaults to `miri-sync`.
ENVIRONMENT VARIABLES
MIRI_SYSROOT:
If already set, the "sysroot setup" step is skipped.
CARGO_EXTRA_FLAGS:
Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
fn main() -> Result<()> {
// We are hand-rolling our own argument parser, since `clap` can't express what we need
// (https://github.com/clap-rs/clap/issues/5055).
let mut args = args::Args::new();
let command = match args.next_raw().as_deref() {
Some("build") => Command::Build { flags: args.remainder() },
Some("check") => Command::Check { flags: args.remainder() },
Some("doc") => Command::Doc { flags: args.remainder() },
Some("test") => {
let mut target = None;
let mut bless = false;
let mut flags = Vec::new();
loop {
if args.get_long_flag("bless")? {
bless = true;
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
flags.push(flag);
} else {
break;
}
}
Command::Test { bless, flags, target }
}
Some("run") => {
let mut dep = false;
let mut verbose = false;
let mut many_seeds = None;
let mut target = None;
let mut edition = None;
let mut flags = Vec::new();
loop {
if args.get_long_flag("dep")? {
dep = true;
} else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
verbose = true;
} else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..64")? {
let (from, to) = val.split_once("..").ok_or_else(|| {
anyhow!("invalid format for `--many-seeds`: expected `from..to`")
})?;
let from: u32 = if from.is_empty() {
0
} else {
from.parse().context("invalid `from` in `--many-seeds=from..to")?
};
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
many_seeds = Some(from..to);
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(val) = args.get_long_opt("edition")? {
edition = Some(val);
} else if let Some(flag) = args.get_other() {
flags.push(flag);
} else {
break;
}
}
Command::Run { dep, verbose, many_seeds, target, edition, flags }
}
Some("fmt") => Command::Fmt { flags: args.remainder() },
Some("clippy") => Command::Clippy { flags: args.remainder() },
Some("install") => Command::Install { flags: args.remainder() },
Some("bench") => {
let mut target = None;
let mut benches = Vec::new();
loop {
if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
benches.push(flag);
} else {
break;
}
}
Command::Bench { target, benches }
}
Some("toolchain") => Command::Toolchain { flags: args.remainder() },
Some("rustc-pull") => {
let commit = args.next_raw();
if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-pull`");
}
Command::RustcPull { commit }
}
Some("rustc-push") => {
let github_user = args.next_raw().ok_or_else(|| {
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
})?;
let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
}
Command::RustcPush { github_user, branch }
}
_ => {
eprintln!("Unknown or missing command. Usage:\n\n{HELP}");
std::process::exit(1);
}
};
command.exec()?;
Ok(())
}