use a little arg-parsing helper for miri-script

This commit is contained in:
Ralf Jung 2024-05-19 18:03:13 +02:00
parent f7520e40f7
commit 9cba160d52
5 changed files with 261 additions and 146 deletions

View File

@ -0,0 +1,136 @@
use std::env;
use std::iter;
use anyhow::{bail, Result};
pub struct Args {
args: iter::Peekable<env::Args>,
/// Set to `true` once we saw a `--`.
terminated: bool,
}
impl Args {
pub fn new() -> Self {
let mut args = Args { args: env::args().peekable(), terminated: false };
args.args.next().unwrap(); // skip program name
args
}
/// Get the next argument without any interpretation.
pub fn next_raw(&mut self) -> Option<String> {
self.args.next()
}
/// Consume a `-$f` flag if present.
pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
if self.terminated {
return Ok(false);
}
if let Some(next) = self.args.peek() {
if let Some(next) = next.strip_prefix("-") {
if let Some(next) = next.strip_prefix(flag) {
if next.is_empty() {
self.args.next().unwrap(); // consume this argument
return Ok(true);
} else {
bail!("`-{flag}` followed by value");
}
}
}
}
Ok(false)
}
/// Consume a `--$name` flag if present.
pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
if self.terminated {
return Ok(false);
}
if let Some(next) = self.args.peek() {
if let Some(next) = next.strip_prefix("--") {
if next == name {
self.args.next().unwrap(); // consume this argument
return Ok(true);
}
}
}
Ok(false)
}
/// Consume a `--$name val` or `--$name=val` option if present.
pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
assert!(!name.is_empty());
if self.terminated {
return Ok(None);
}
let Some(next) = self.args.peek() else { return Ok(None) };
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
// Starts with `--flag`.
Ok(if let Some(val) = next.strip_prefix("=") {
// `--flag=val` form
let val = val.into();
self.args.next().unwrap(); // consume this argument
Some(val)
} else if next.is_empty() {
// `--flag val` form
self.args.next().unwrap(); // consume this argument
let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
Some(val)
} else {
// Some unrelated flag, like `--flag-more` or so.
None
})
}
/// Consume a `--$name=val` or `--$name` option if present; the latter
/// produces a default value. (`--$name val` is *not* accepted for this form
/// of argument, it understands `val` already as the next argument!)
pub fn get_long_opt_with_default(
&mut self,
name: &str,
default: &str,
) -> Result<Option<String>> {
assert!(!name.is_empty());
if self.terminated {
return Ok(None);
}
let Some(next) = self.args.peek() else { return Ok(None) };
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
// Starts with `--flag`.
Ok(if let Some(val) = next.strip_prefix("=") {
// `--flag=val` form
let val = val.into();
self.args.next().unwrap(); // consume this argument
Some(val)
} else if next.is_empty() {
// `--flag` form
self.args.next().unwrap(); // consume this argument
Some(default.into())
} else {
// Some unrelated flag, like `--flag-more` or so.
None
})
}
/// Returns the next free argument or uninterpreted flag, or `None` if there are no more
/// arguments left. `--` is returned as well, but it is interpreted in the sense that no more
/// flags will be parsed after this.
pub fn get_other(&mut self) -> Option<String> {
if self.terminated {
return self.args.next();
}
let next = self.args.next()?;
if next == "--" {
self.terminated = true; // don't parse any more flags
// This is where our parser is special, we do yield the `--`.
}
Some(next)
}
/// Return the rest of the aguments entirely unparsed.
pub fn remainder(self) -> Vec<String> {
self.args.collect()
}
}

View File

@ -25,7 +25,11 @@ impl MiriEnv {
/// Returns the location of the sysroot. /// Returns the location of the sysroot.
/// ///
/// If the target is None the sysroot will be built for the host machine. /// If the target is None the sysroot will be built for the host machine.
fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result<PathBuf> { fn build_miri_sysroot(
&mut self,
quiet: bool,
target: Option<impl AsRef<OsStr>>,
) -> Result<PathBuf> {
if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
// Sysroot already set, use that. // Sysroot already set, use that.
return Ok(miri_sysroot.into()); return Ok(miri_sysroot.into());
@ -37,14 +41,17 @@ impl MiriEnv {
self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?; self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
self.build(&manifest_path, &[], quiet)?; self.build(&manifest_path, &[], quiet)?;
let target_flag = let target_flag = if let Some(target) = &target {
if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] }; vec![OsStr::new("--target"), target.as_ref()]
} else {
vec![]
};
let target_flag = &target_flag; let target_flag = &target_flag;
if !quiet { if !quiet {
eprint!("$ cargo miri setup"); eprint!("$ cargo miri setup");
if let Some(target) = target { if let Some(target) = &target {
eprint!(" --target {target}", target = target.to_string_lossy()); eprint!(" --target {target}", target = target.as_ref().to_string_lossy());
} }
eprintln!(); eprintln!();
} }
@ -157,8 +164,8 @@ impl Command {
Command::Build { flags } => Self::build(flags), Command::Build { flags } => Self::build(flags),
Command::Check { flags } => Self::check(flags), Command::Check { flags } => Self::check(flags),
Command::Test { bless, flags, target } => Self::test(bless, flags, target), Command::Test { bless, flags, target } => Self::test(bless, flags, target),
Command::Run { dep, verbose, many_seeds, flags } => Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
Self::run(dep, verbose, many_seeds, flags), Self::run(dep, verbose, many_seeds, target, edition, flags),
Command::Fmt { flags } => Self::fmt(flags), Command::Fmt { flags } => Self::fmt(flags),
Command::Clippy { flags } => Self::clippy(flags), Command::Clippy { flags } => Self::clippy(flags),
Command::Cargo { flags } => Self::cargo(flags), Command::Cargo { flags } => Self::cargo(flags),
@ -169,7 +176,7 @@ impl Command {
} }
} }
fn toolchain(flags: Vec<OsString>) -> Result<()> { fn toolchain(flags: Vec<String>) -> Result<()> {
// Make sure rustup-toolchain-install-master is installed. // Make sure rustup-toolchain-install-master is installed.
which::which("rustup-toolchain-install-master") which::which("rustup-toolchain-install-master")
.context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?; .context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
@ -364,7 +371,7 @@ impl Command {
Ok(()) Ok(())
} }
fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> { fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
// The hyperfine to use // The hyperfine to use
let hyperfine = env::var("HYPERFINE"); let hyperfine = env::var("HYPERFINE");
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none"); let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
@ -378,14 +385,14 @@ impl Command {
let sh = Shell::new()?; let sh = Shell::new()?;
sh.change_dir(miri_dir()?); sh.change_dir(miri_dir()?);
let benches_dir = "bench-cargo-miri"; let benches_dir = "bench-cargo-miri";
let benches = if benches.is_empty() { let benches: Vec<OsString> = if benches.is_empty() {
sh.read_dir(benches_dir)? sh.read_dir(benches_dir)?
.into_iter() .into_iter()
.filter(|path| path.is_dir()) .filter(|path| path.is_dir())
.map(Into::into) .map(Into::into)
.collect() .collect()
} else { } else {
benches.to_owned() benches.into_iter().map(Into::into).collect()
}; };
let target_flag = if let Some(target) = target { let target_flag = if let Some(target) = target {
let mut flag = OsString::from("--target="); let mut flag = OsString::from("--target=");
@ -409,28 +416,28 @@ impl Command {
Ok(()) Ok(())
} }
fn install(flags: Vec<OsString>) -> Result<()> { fn install(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
e.install_to_sysroot(e.miri_dir.clone(), &flags)?; e.install_to_sysroot(e.miri_dir.clone(), &flags)?;
e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?; e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?;
Ok(()) Ok(())
} }
fn build(flags: Vec<OsString>) -> Result<()> { fn build(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?; e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?;
e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?; e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?;
Ok(()) Ok(())
} }
fn check(flags: Vec<OsString>) -> Result<()> { fn check(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?; e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?;
e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?; e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
Ok(()) Ok(())
} }
fn clippy(flags: Vec<OsString>) -> Result<()> { fn clippy(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?; e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?;
e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?; e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
@ -438,7 +445,7 @@ impl Command {
Ok(()) Ok(())
} }
fn cargo(flags: Vec<OsString>) -> Result<()> { fn cargo(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
let toolchain = &e.toolchain; let toolchain = &e.toolchain;
// We carefully kept the working dir intact, so this will run cargo *on the workspace in the // We carefully kept the working dir intact, so this will run cargo *on the workspace in the
@ -447,7 +454,7 @@ impl Command {
Ok(()) Ok(())
} }
fn test(bless: bool, mut flags: Vec<OsString>, target: Option<OsString>) -> Result<()> { fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
let mut e = MiriEnv::new()?; let mut e = MiriEnv::new()?;
// Prepare a sysroot. // Prepare a sysroot.
@ -475,21 +482,30 @@ impl Command {
dep: bool, dep: bool,
verbose: bool, verbose: bool,
many_seeds: Option<Range<u32>>, many_seeds: Option<Range<u32>>,
mut flags: Vec<OsString>, target: Option<String>,
edition: Option<String>,
flags: Vec<String>,
) -> Result<()> { ) -> Result<()> {
let mut e = MiriEnv::new()?; let mut e = MiriEnv::new()?;
let target = arg_flag_value(&flags, "--target"); // More flags that we will pass before `flags`
// (because `flags` may contain `--`).
let mut early_flags = Vec::<OsString>::new();
// Scan for "--edition", set one ourselves if that flag is not present. // Add target, edition to flags.
let have_edition = arg_flag_value(&flags, "--edition").is_some(); if let Some(target) = &target {
if !have_edition { early_flags.push("--target".into());
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` early_flags.push(target.into());
} }
if verbose {
early_flags.push("--verbose".into());
}
early_flags.push("--edition".into());
early_flags.push(edition.as_deref().unwrap_or("2021").into());
// Prepare a sysroot, and add it to the flags. // Prepare a sysroot, add it to the flags.
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?; let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
flags.push("--sysroot".into()); early_flags.push("--sysroot".into());
flags.push(miri_sysroot.into()); early_flags.push(miri_sysroot.into());
// Compute everything needed to run the actual command. Also add MIRIFLAGS. // Compute everything needed to run the actual command. Also add MIRIFLAGS.
let miri_manifest = path!(e.miri_dir / "Cargo.toml"); let miri_manifest = path!(e.miri_dir / "Cargo.toml");
@ -515,7 +531,7 @@ impl Command {
}; };
cmd.set_quiet(!verbose); cmd.set_quiet(!verbose);
// Add Miri flags // Add Miri flags
let cmd = cmd.args(&miri_flags).args(seed_flag).args(&flags); let cmd = cmd.args(&miri_flags).args(&seed_flag).args(&early_flags).args(&flags);
// And run the thing. // And run the thing.
Ok(cmd.run()?) Ok(cmd.run()?)
}; };
@ -534,7 +550,7 @@ impl Command {
Ok(()) Ok(())
} }
fn fmt(flags: Vec<OsString>) -> Result<()> { fn fmt(flags: Vec<String>) -> Result<()> {
use itertools::Itertools; use itertools::Itertools;
let e = MiriEnv::new()?; let e = MiriEnv::new()?;
@ -556,6 +572,6 @@ impl Command {
.filter_ok(|item| item.file_type().is_file()) .filter_ok(|item| item.file_type().is_file())
.map_ok(|item| item.into_path()); .map_ok(|item| item.into_path());
e.format_files(files, &e.toolchain[..], &config_path, &flags[..]) e.format_files(files, &e.toolchain[..], &config_path, &flags)
} }
} }

View File

@ -1,10 +1,10 @@
#![allow(clippy::needless_question_mark)] #![allow(clippy::needless_question_mark)]
mod args;
mod commands; mod commands;
mod util; mod util;
use std::ffi::OsString; use std::ops::Range;
use std::{env, ops::Range};
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
@ -16,26 +16,26 @@ pub enum Command {
/// sysroot, to prevent conflicts with other toolchains. /// sysroot, to prevent conflicts with other toolchains.
Install { Install {
/// Flags that are passed through to `cargo install`. /// Flags that are passed through to `cargo install`.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Just build miri. /// Just build miri.
Build { Build {
/// Flags that are passed through to `cargo build`. /// Flags that are passed through to `cargo build`.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Just check miri. /// Just check miri.
Check { Check {
/// Flags that are passed through to `cargo check`. /// Flags that are passed through to `cargo check`.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Build miri, set up a sysroot and then run the test suite. /// Build miri, set up a sysroot and then run the test suite.
Test { Test {
bless: bool, bless: bool,
/// The cross-interpretation target. /// The cross-interpretation target.
/// If none then the host is the target. /// If none then the host is the target.
target: Option<OsString>, target: Option<String>,
/// Flags that are passed through to the test harness. /// Flags that are passed through to the test harness.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Build miri, set up a sysroot and then run the driver with the given <flags>. /// Build miri, set up a sysroot and then run the driver with the given <flags>.
/// (Also respects MIRIFLAGS environment variable.) /// (Also respects MIRIFLAGS environment variable.)
@ -43,33 +43,35 @@ pub enum Command {
dep: bool, dep: bool,
verbose: bool, verbose: bool,
many_seeds: Option<Range<u32>>, many_seeds: Option<Range<u32>>,
target: Option<String>,
edition: Option<String>,
/// Flags that are passed through to `miri`. /// Flags that are passed through to `miri`.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Format all sources and tests. /// Format all sources and tests.
Fmt { Fmt {
/// Flags that are passed through to `rustfmt`. /// Flags that are passed through to `rustfmt`.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Runs clippy on all sources. /// Runs clippy on all sources.
Clippy { Clippy {
/// Flags that are passed through to `cargo clippy`. /// Flags that are passed through to `cargo clippy`.
flags: Vec<OsString>, flags: Vec<String>,
}, },
/// Runs just `cargo <flags>` with the Miri-specific environment variables. /// Runs just `cargo <flags>` with the Miri-specific environment variables.
/// Mainly meant to be invoked by rust-analyzer. /// Mainly meant to be invoked by rust-analyzer.
Cargo { flags: Vec<OsString> }, Cargo { flags: Vec<String> },
/// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
Bench { Bench {
target: Option<OsString>, target: Option<String>,
/// List of benchmarks to run. By default all benchmarks are run. /// List of benchmarks to run. By default all benchmarks are run.
benches: Vec<OsString>, benches: Vec<String>,
}, },
/// Update and activate the rustup toolchain 'miri' to the commit given in the /// Update and activate the rustup toolchain 'miri' to the commit given in the
/// `rust-version` file. /// `rust-version` file.
/// `rustup-toolchain-install-master` must be installed for this to work. Any extra /// `rustup-toolchain-install-master` must be installed for this to work. Any extra
/// flags are passed to `rustup-toolchain-install-master`. /// flags are passed to `rustup-toolchain-install-master`.
Toolchain { flags: Vec<OsString> }, Toolchain { flags: Vec<String> },
/// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest /// 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 /// 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. /// next `./miri toolchain` will install the rustc that just got pulled.
@ -145,113 +147,95 @@ Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
fn main() -> Result<()> { fn main() -> Result<()> {
// We are hand-rolling our own argument parser, since `clap` can't express what we need // We are hand-rolling our own argument parser, since `clap` can't express what we need
// (https://github.com/clap-rs/clap/issues/5055). // (https://github.com/clap-rs/clap/issues/5055).
let mut args = env::args_os().peekable(); let mut args = args::Args::new();
args.next().unwrap(); // skip program name let command = match args.next_raw().as_deref() {
let command = match args.next().and_then(|s| s.into_string().ok()).as_deref() { Some("build") => Command::Build { flags: args.remainder() },
Some("build") => Command::Build { flags: args.collect() }, Some("check") => Command::Check { flags: args.remainder() },
Some("check") => Command::Check { flags: args.collect() },
Some("test") => { Some("test") => {
let mut target = None; let mut target = None;
let mut bless = false; let mut bless = false;
let mut flags = Vec::new();
while let Some(arg) = args.peek().and_then(|s| s.to_str()) { loop {
match arg { if args.get_long_flag("bless")? {
"--bless" => bless = true, bless = true;
"--target" => { } else if let Some(val) = args.get_long_opt("target")? {
// Skip "--target" target = Some(val);
args.next().unwrap(); } else if let Some(flag) = args.get_other() {
// Next argument is the target triple. flags.push(flag);
let val = args.peek().ok_or_else(|| { } else {
anyhow!("`--target` must be followed by target triple") break;
})?;
target = Some(val.to_owned());
} }
// Only parse the leading flags.
_ => break,
} }
Command::Test { bless, flags, target }
// Consume the flag, look at the next one.
args.next().unwrap();
}
Command::Test { bless, flags: args.collect(), target }
} }
Some("run") => { Some("run") => {
let mut dep = false; let mut dep = false;
let mut verbose = false; let mut verbose = false;
let mut many_seeds = None; let mut many_seeds = None;
while let Some(arg) = args.peek().and_then(|s| s.to_str()) { let mut target = None;
if arg == "--dep" { let mut edition = None;
let mut flags = Vec::new();
loop {
if args.get_long_flag("dep")? {
dep = true; dep = true;
} else if arg == "-v" || arg == "--verbose" { } else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
verbose = true; verbose = true;
} else if arg == "--many-seeds" { } else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..256")? {
many_seeds = Some(0..256);
} else if let Some(val) = arg.strip_prefix("--many-seeds=") {
let (from, to) = val.split_once("..").ok_or_else(|| { let (from, to) = val.split_once("..").ok_or_else(|| {
anyhow!("invalid format for `--many-seeds`: expected `from..to`") anyhow!("invalid format for `--many-seeds-range`: expected `from..to`")
})?; })?;
let from: u32 = if from.is_empty() { let from: u32 = if from.is_empty() {
0 0
} else { } else {
from.parse().context("invalid `from` in `--many-seeds=from..to")? from.parse().context("invalid `from` in `--many-seeds-range=from..to")?
}; };
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?; let to: u32 =
to.parse().context("invalid `to` in `--many-seeds-range=from..to")?;
many_seeds = Some(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 { } else {
break; // not for us break;
} }
// Consume the flag, look at the next one.
args.next().unwrap();
} }
Command::Run { dep, verbose, many_seeds, flags: args.collect() } Command::Run { dep, verbose, many_seeds, target, edition, flags }
} }
Some("fmt") => Command::Fmt { flags: args.collect() }, Some("fmt") => Command::Fmt { flags: args.remainder() },
Some("clippy") => Command::Clippy { flags: args.collect() }, Some("clippy") => Command::Clippy { flags: args.remainder() },
Some("cargo") => Command::Cargo { flags: args.collect() }, Some("cargo") => Command::Cargo { flags: args.remainder() },
Some("install") => Command::Install { flags: args.collect() }, Some("install") => Command::Install { flags: args.remainder() },
Some("bench") => { Some("bench") => {
let mut target = None; let mut target = None;
while let Some(arg) = args.peek().and_then(|s| s.to_str()) { let mut benches = Vec::new();
match arg { loop {
"--target" => { if let Some(val) = args.get_long_opt("target")? {
// Skip "--target" target = Some(val);
args.next().unwrap(); } else if let Some(flag) = args.get_other() {
// Next argument is the target triple. benches.push(flag);
let val = args.peek().ok_or_else(|| { } else {
anyhow!("`--target` must be followed by target triple") break;
})?;
target = Some(val.to_owned());
} }
// Only parse the leading flags.
_ => break,
} }
Command::Bench { target, benches }
// Consume the flag, look at the next one.
args.next().unwrap();
} }
Some("toolchain") => Command::Toolchain { flags: args.remainder() },
Command::Bench { target, benches: args.collect() }
}
Some("toolchain") => Command::Toolchain { flags: args.collect() },
Some("rustc-pull") => { Some("rustc-pull") => {
let commit = args.next().map(|a| a.to_string_lossy().into_owned()); let commit = args.next_raw();
if args.next().is_some() { if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-pull`"); bail!("Too many arguments for `./miri rustc-pull`");
} }
Command::RustcPull { commit } Command::RustcPull { commit }
} }
Some("rustc-push") => { Some("rustc-push") => {
let github_user = args let github_user = args.next_raw().ok_or_else(|| {
.next()
.ok_or_else(|| {
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`") anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
})? })?;
.to_string_lossy() let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
.into_owned(); if args.next_raw().is_some() {
let branch =
args.next().unwrap_or_else(|| "miri-sync".into()).to_string_lossy().into_owned();
if args.next().is_some() {
bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`"); bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
} }
Command::RustcPush { github_user, branch } Command::RustcPush { github_user, branch }

View File

@ -27,30 +27,6 @@ pub fn flagsplit(flags: &str) -> Vec<String> {
flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
} }
pub fn arg_flag_value(
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
flag: &str,
) -> Option<OsString> {
let mut args = args.into_iter();
while let Some(arg) = args.next() {
let arg = arg.as_ref();
if arg == "--" {
return None;
}
let Some(arg) = arg.to_str() else {
// Skip non-UTF-8 arguments.
continue;
};
if arg == flag {
// Next one is the value.
return Some(args.next()?.as_ref().to_owned());
} else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) {
return Some(val.to_owned().into());
}
}
None
}
/// Some extra state we track for building Miri, such as the right RUSTFLAGS. /// Some extra state we track for building Miri, such as the right RUSTFLAGS.
pub struct MiriEnv { pub struct MiriEnv {
/// miri_dir is the root of the miri repository checkout we are working in. /// miri_dir is the root of the miri repository checkout we are working in.
@ -133,7 +109,7 @@ impl MiriEnv {
pub fn build( pub fn build(
&self, &self,
manifest_path: impl AsRef<OsStr>, manifest_path: impl AsRef<OsStr>,
args: &[OsString], args: &[String],
quiet: bool, quiet: bool,
) -> Result<()> { ) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self; let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
@ -149,21 +125,21 @@ impl MiriEnv {
Ok(()) Ok(())
} }
pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> { pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self; let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}") cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
.run()?; .run()?;
Ok(()) Ok(())
} }
pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> { pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self; let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}") cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
.run()?; .run()?;
Ok(()) Ok(())
} }
pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> { pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self; let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
cmd!( cmd!(
self.sh, self.sh,
@ -181,7 +157,7 @@ impl MiriEnv {
files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>, files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>,
toolchain: &str, toolchain: &str,
config_path: &Path, config_path: &Path,
flags: &[OsString], flags: &[String],
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
use itertools::Itertools; use itertools::Itertools;

View File

@ -405,9 +405,12 @@ fn main() {
let mut rustc_args = vec![]; let mut rustc_args = vec![];
let mut after_dashdash = false; let mut after_dashdash = false;
// If user has explicitly enabled/disabled isolation // If user has explicitly enabled/disabled isolation
let mut isolation_enabled: Option<bool> = None; let mut isolation_enabled: Option<bool> = None;
// Note that we require values to be given with `=`, not with a space.
// This matches how rustc parses `-Z`.
// However, unlike rustc we do not accept a space after `-Z`.
for arg in args { for arg in args {
if rustc_args.is_empty() { if rustc_args.is_empty() {
// Very first arg: binary name. // Very first arg: binary name.