Skip to content

Commit c873bab

Browse files
committed
Auto merge of #3621 - RalfJung:argparse, r=RalfJung
use a little arg-parsing helper for miri-script
2 parents f763474 + f834721 commit c873bab

File tree

5 files changed

+259
-143
lines changed

5 files changed

+259
-143
lines changed

miri-script/src/args.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use std::env;
2+
use std::iter;
3+
4+
use anyhow::{bail, Result};
5+
6+
pub struct Args {
7+
args: iter::Peekable<env::Args>,
8+
/// Set to `true` once we saw a `--`.
9+
terminated: bool,
10+
}
11+
12+
impl Args {
13+
pub fn new() -> Self {
14+
let mut args = Args { args: env::args().peekable(), terminated: false };
15+
args.next().unwrap(); // skip program name
16+
args
17+
}
18+
19+
pub fn next(&mut self) -> Option<String> {
20+
self.args.next()
21+
}
22+
23+
/// Consume a `-$f` flag if present.
24+
pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
25+
if self.terminated {
26+
return Ok(false);
27+
}
28+
if let Some(next) = self.args.peek() {
29+
if let Some(next) = next.strip_prefix("-") {
30+
if let Some(next) = next.strip_prefix(flag) {
31+
if next.is_empty() {
32+
self.args.next().unwrap(); // consume this argument
33+
return Ok(true);
34+
} else {
35+
bail!("`-{flag}` followed by value");
36+
}
37+
}
38+
}
39+
}
40+
Ok(false)
41+
}
42+
43+
/// Consume a `--$name` flag if present.
44+
pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
45+
if self.terminated {
46+
return Ok(false);
47+
}
48+
if let Some(next) = self.args.peek() {
49+
if let Some(next) = next.strip_prefix("--") {
50+
if next == name {
51+
self.args.next().unwrap(); // consume this argument
52+
return Ok(true);
53+
}
54+
}
55+
}
56+
Ok(false)
57+
}
58+
59+
/// Consume a `--$name val` or `--$name=val` option if present.
60+
pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
61+
assert!(!name.is_empty());
62+
if self.terminated {
63+
return Ok(None);
64+
}
65+
let Some(next) = self.args.peek() else { return Ok(None) };
66+
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
67+
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
68+
// Starts with `--flag`.
69+
Ok(if let Some(val) = next.strip_prefix("=") {
70+
// `--flag=val` form
71+
let val = val.into();
72+
self.args.next().unwrap(); // consume this argument
73+
Some(val)
74+
} else if next.is_empty() {
75+
// `--flag val` form
76+
self.args.next().unwrap(); // consume this argument
77+
let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
78+
Some(val)
79+
} else {
80+
// Some unrelated flag, like `--flag-more` or so.
81+
None
82+
})
83+
}
84+
85+
/// Consume a `--$name=val` or `--$name` option if present; the latter
86+
/// produces a default value. (`--$name val` is *not* accepted for this form
87+
/// of argument, it understands `val` already as the next argument!)
88+
pub fn get_long_opt_with_default(
89+
&mut self,
90+
name: &str,
91+
default: &str,
92+
) -> Result<Option<String>> {
93+
assert!(!name.is_empty());
94+
if self.terminated {
95+
return Ok(None);
96+
}
97+
let Some(next) = self.args.peek() else { return Ok(None) };
98+
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
99+
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
100+
// Starts with `--flag`.
101+
Ok(if let Some(val) = next.strip_prefix("=") {
102+
// `--flag=val` form
103+
let val = val.into();
104+
self.args.next().unwrap(); // consume this argument
105+
Some(val)
106+
} else if next.is_empty() {
107+
// `--flag` form
108+
self.args.next().unwrap(); // consume this argument
109+
Some(default.into())
110+
} else {
111+
// Some unrelated flag, like `--flag-more` or so.
112+
None
113+
})
114+
}
115+
116+
/// Returns the next free argument, or `None` if there are no more arguments left.
117+
pub fn get_free(&mut self) -> Option<String> {
118+
if self.terminated {
119+
return self.args.next();
120+
}
121+
let next = self.args.next()?;
122+
if next == "--" {
123+
self.terminated = true; // don't parse any more flags
124+
// This is where our parser is special, we do yield the `--`.
125+
}
126+
Some(next)
127+
}
128+
129+
/// Return the rest of the aguments entirely unparsed.
130+
pub fn remainder(self) -> Vec<String> {
131+
self.args.collect()
132+
}
133+
}

miri-script/src/commands.rs

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ impl MiriEnv {
2525
/// Returns the location of the sysroot.
2626
///
2727
/// If the target is None the sysroot will be built for the host machine.
28-
fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result<PathBuf> {
28+
fn build_miri_sysroot(
29+
&mut self,
30+
quiet: bool,
31+
target: Option<impl AsRef<OsStr>>,
32+
) -> Result<PathBuf> {
2933
if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
3034
// Sysroot already set, use that.
3135
return Ok(miri_sysroot.into());
@@ -37,14 +41,17 @@ impl MiriEnv {
3741
self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
3842
self.build(&manifest_path, &[], quiet)?;
3943

40-
let target_flag =
41-
if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] };
44+
let target_flag = if let Some(target) = &target {
45+
vec![OsStr::new("--target"), target.as_ref()]
46+
} else {
47+
vec![]
48+
};
4249
let target_flag = &target_flag;
4350

4451
if !quiet {
4552
eprint!("$ cargo miri setup");
46-
if let Some(target) = target {
47-
eprint!(" --target {target}", target = target.to_string_lossy());
53+
if let Some(target) = &target {
54+
eprint!(" --target {target}", target = target.as_ref().to_string_lossy());
4855
}
4956
eprintln!();
5057
}
@@ -157,8 +164,8 @@ impl Command {
157164
Command::Build { flags } => Self::build(flags),
158165
Command::Check { flags } => Self::check(flags),
159166
Command::Test { bless, flags, target } => Self::test(bless, flags, target),
160-
Command::Run { dep, verbose, many_seeds, flags } =>
161-
Self::run(dep, verbose, many_seeds, flags),
167+
Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
168+
Self::run(dep, verbose, many_seeds, target, edition, flags),
162169
Command::Fmt { flags } => Self::fmt(flags),
163170
Command::Clippy { flags } => Self::clippy(flags),
164171
Command::Cargo { flags } => Self::cargo(flags),
@@ -169,7 +176,7 @@ impl Command {
169176
}
170177
}
171178

172-
fn toolchain(flags: Vec<OsString>) -> Result<()> {
179+
fn toolchain(flags: Vec<String>) -> Result<()> {
173180
// Make sure rustup-toolchain-install-master is installed.
174181
which::which("rustup-toolchain-install-master")
175182
.context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
@@ -364,7 +371,7 @@ impl Command {
364371
Ok(())
365372
}
366373

367-
fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> {
374+
fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
368375
// The hyperfine to use
369376
let hyperfine = env::var("HYPERFINE");
370377
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
@@ -378,14 +385,14 @@ impl Command {
378385
let sh = Shell::new()?;
379386
sh.change_dir(miri_dir()?);
380387
let benches_dir = "bench-cargo-miri";
381-
let benches = if benches.is_empty() {
388+
let benches: Vec<OsString> = if benches.is_empty() {
382389
sh.read_dir(benches_dir)?
383390
.into_iter()
384391
.filter(|path| path.is_dir())
385392
.map(Into::into)
386393
.collect()
387394
} else {
388-
benches.to_owned()
395+
benches.into_iter().map(Into::into).collect()
389396
};
390397
let target_flag = if let Some(target) = target {
391398
let mut flag = OsString::from("--target=");
@@ -409,36 +416,36 @@ impl Command {
409416
Ok(())
410417
}
411418

412-
fn install(flags: Vec<OsString>) -> Result<()> {
419+
fn install(flags: Vec<String>) -> Result<()> {
413420
let e = MiriEnv::new()?;
414421
e.install_to_sysroot(e.miri_dir.clone(), &flags)?;
415422
e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?;
416423
Ok(())
417424
}
418425

419-
fn build(flags: Vec<OsString>) -> Result<()> {
426+
fn build(flags: Vec<String>) -> Result<()> {
420427
let e = MiriEnv::new()?;
421428
e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?;
422429
e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?;
423430
Ok(())
424431
}
425432

426-
fn check(flags: Vec<OsString>) -> Result<()> {
433+
fn check(flags: Vec<String>) -> Result<()> {
427434
let e = MiriEnv::new()?;
428435
e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?;
429436
e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
430437
Ok(())
431438
}
432439

433-
fn clippy(flags: Vec<OsString>) -> Result<()> {
440+
fn clippy(flags: Vec<String>) -> Result<()> {
434441
let e = MiriEnv::new()?;
435442
e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?;
436443
e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
437444
e.clippy(path!(e.miri_dir / "miri-script" / "Cargo.toml"), &flags)?;
438445
Ok(())
439446
}
440447

441-
fn cargo(flags: Vec<OsString>) -> Result<()> {
448+
fn cargo(flags: Vec<String>) -> Result<()> {
442449
let e = MiriEnv::new()?;
443450
let toolchain = &e.toolchain;
444451
// We carefully kept the working dir intact, so this will run cargo *on the workspace in the
@@ -447,7 +454,7 @@ impl Command {
447454
Ok(())
448455
}
449456

450-
fn test(bless: bool, mut flags: Vec<OsString>, target: Option<OsString>) -> Result<()> {
457+
fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
451458
let mut e = MiriEnv::new()?;
452459

453460
// Prepare a sysroot.
@@ -475,21 +482,30 @@ impl Command {
475482
dep: bool,
476483
verbose: bool,
477484
many_seeds: Option<Range<u32>>,
478-
mut flags: Vec<OsString>,
485+
target: Option<String>,
486+
edition: Option<String>,
487+
flags: Vec<String>,
479488
) -> Result<()> {
480489
let mut e = MiriEnv::new()?;
481-
let target = arg_flag_value(&flags, "--target");
482-
483-
// Scan for "--edition", set one ourselves if that flag is not present.
484-
let have_edition = arg_flag_value(&flags, "--edition").is_some();
485-
if !have_edition {
486-
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
490+
// More flags that we will pass before `flags`
491+
// (because `flags` may contain `--`).
492+
let mut early_flags = Vec::<OsString>::new();
493+
494+
// Add target, edition to flags.
495+
if let Some(target) = &target {
496+
early_flags.push("--target".into());
497+
early_flags.push(target.into());
498+
}
499+
if verbose {
500+
early_flags.push("--verbose".into());
487501
}
502+
early_flags.push("--edition".into());
503+
early_flags.push(edition.as_deref().unwrap_or("2021").into());
488504

489-
// Prepare a sysroot, and add it to the flags.
505+
// Prepare a sysroot, add it to the flags.
490506
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
491-
flags.push("--sysroot".into());
492-
flags.push(miri_sysroot.into());
507+
early_flags.push("--sysroot".into());
508+
early_flags.push(miri_sysroot.into());
493509

494510
// Compute everything needed to run the actual command. Also add MIRIFLAGS.
495511
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
@@ -515,7 +531,7 @@ impl Command {
515531
};
516532
cmd.set_quiet(!verbose);
517533
// Add Miri flags
518-
let cmd = cmd.args(&miri_flags).args(seed_flag).args(&flags);
534+
let cmd = cmd.args(&miri_flags).args(&seed_flag).args(&early_flags).args(&flags);
519535
// And run the thing.
520536
Ok(cmd.run()?)
521537
};
@@ -534,7 +550,7 @@ impl Command {
534550
Ok(())
535551
}
536552

537-
fn fmt(flags: Vec<OsString>) -> Result<()> {
553+
fn fmt(flags: Vec<String>) -> Result<()> {
538554
use itertools::Itertools;
539555

540556
let e = MiriEnv::new()?;
@@ -556,6 +572,6 @@ impl Command {
556572
.filter_ok(|item| item.file_type().is_file())
557573
.map_ok(|item| item.into_path());
558574

559-
e.format_files(files, &e.toolchain[..], &config_path, &flags[..])
575+
e.format_files(files, &e.toolchain[..], &config_path, &flags)
560576
}
561577
}

0 commit comments

Comments
 (0)