about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-07-30 17:28:23 +0200
committerRalf Jung <post@ralfj.de>2023-07-31 09:54:05 +0200
commit3f952f4508c0216575b246e8279357cf99475e01 (patch)
treedd1227b05de98f122b1b5c184e1137780cbe5acf
parent7f2eca6a34da3e03f0ced71858bc76a986d5d886 (diff)
downloadrust-3f952f4508c0216575b246e8279357cf99475e01.tar.gz
rust-3f952f4508c0216575b246e8279357cf99475e01.zip
miri-script refactor
-rw-r--r--src/tools/miri/.github/workflows/ci.yml2
-rw-r--r--src/tools/miri/CONTRIBUTING.md2
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs3
-rw-r--r--src/tools/miri/cargo-miri/src/util.rs5
-rwxr-xr-xsrc/tools/miri/miri4
-rwxr-xr-xsrc/tools/miri/miri-script/miri4
-rw-r--r--src/tools/miri/miri-script/src/arg.rs104
-rw-r--r--src/tools/miri/miri-script/src/commands.rs734
-rw-r--r--src/tools/miri/miri-script/src/main.rs156
-rw-r--r--src/tools/miri/miri-script/src/util.rs153
10 files changed, 553 insertions, 614 deletions
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 042bb9bbd2c..8ced9fa86be 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -130,7 +130,7 @@ jobs:
       - name: clippy
         run: ./miri clippy -- -D warnings
       - name: rustdoc
-        run: RUSTDOCFLAGS="-Dwarnings" cargo doc --document-private-items
+        run: RUSTDOCFLAGS="-Dwarnings" ./miri cargo doc --document-private-items
 
   # These jobs doesn't actually test anything, but they're only used to tell
   # bors the build completed, as there is no practical way to detect when a
diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md
index bcdb623b090..b67e7103fd0 100644
--- a/src/tools/miri/CONTRIBUTING.md
+++ b/src/tools/miri/CONTRIBUTING.md
@@ -107,7 +107,7 @@ evaluation error was originally raised.
 ### UI testing
 
 We use ui-testing in Miri, meaning we generate `.stderr` and `.stdout` files for the output
-produced by Miri. You can use `./miri bless` to automatically (re)generate these files when
+produced by Miri. You can use `./miri test --bless` to automatically (re)generate these files when
 you add new tests or change how Miri presents certain output.
 
 Note that when you also use `MIRIFLAGS` to change optimizations and similar, the ui output
diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index 465e4a1b2d2..d74e0c5157d 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -538,8 +538,7 @@ pub fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: Runner
     }
     // Respect `MIRIFLAGS`.
     if let Ok(a) = env::var("MIRIFLAGS") {
-        // This code is taken from `RUSTFLAGS` handling in cargo.
-        let args = a.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string);
+        let args = flagsplit(&a);
         cmd.args(args);
     }
 
diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs
index 4c19ed97fb8..0e3b04c0d88 100644
--- a/src/tools/miri/cargo-miri/src/util.rs
+++ b/src/tools/miri/cargo-miri/src/util.rs
@@ -114,6 +114,11 @@ pub fn cargo() -> Command {
     Command::new(env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")))
 }
 
+pub fn flagsplit(flags: &str) -> Vec<String> {
+    // This code is taken from `RUSTFLAGS` handling in cargo.
+    flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
+}
+
 /// Execute the `Command`, where possible by replacing the current process with a new process
 /// described by the `Command`. Then exit this process with the exit code of the new process.
 pub fn exec(mut cmd: Command) -> ! {
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index 7412df69bd6..c816a4bb06b 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -2,5 +2,5 @@
 set -e
 # Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
 # rustup (that sets it's own environmental variables), which is undesirable.
-cargo build --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml
-"$(dirname "$0")"/miri-script/target/debug/miri-script $@
\ No newline at end of file
+cargo build -q --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml
+"$(dirname "$0")"/miri-script/target/debug/miri-script "$@"
diff --git a/src/tools/miri/miri-script/miri b/src/tools/miri/miri-script/miri
new file mode 100755
index 00000000000..cf3ad06788a
--- /dev/null
+++ b/src/tools/miri/miri-script/miri
@@ -0,0 +1,4 @@
+#!/bin/sh
+# RA invokes `./miri cargo ...` for each workspace, so we need to forward that to the main `miri`
+# script. See <https://github.com/rust-analyzer/rust-analyzer/issues/10793>.
+exec "$(dirname "$0")"/../miri "$@"
diff --git a/src/tools/miri/miri-script/src/arg.rs b/src/tools/miri/miri-script/src/arg.rs
deleted file mode 100644
index 24a5204e043..00000000000
--- a/src/tools/miri/miri-script/src/arg.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-use clap::{Parser, Subcommand};
-use std::ffi::OsString;
-
-#[derive(Parser, Clone, Debug)]
-#[command(author, about, long_about = None)]
-pub struct Cli {
-    #[command(subcommand)]
-    pub commands: Subcommands,
-}
-
-#[derive(Subcommand, Clone, Debug)]
-pub enum Subcommands {
-    /// 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`.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Just build miri.
-    Build {
-        /// Flags that are passed through to `cargo build`.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Just check miri.
-    Check {
-        /// Flags that are passed through to `cargo check`.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Build miri, set up a sysroot and then run the test suite.
-    Test {
-        #[arg(long, default_value_t = false)]
-        bless: bool,
-        /// Flags that are passed through to `cargo test`.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Build miri, set up a sysroot and then run the driver with the given <flags>.
-    /// (Also respects MIRIFLAGS environment variable.)
-    Run {
-        #[arg(long, default_value_t = false)]
-        dep: bool,
-        /// Flags that are passed through to `miri`
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Format all sources and tests.
-    Fmt {
-        /// Flags that are passed through to `rustfmt`.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Runs clippy on all sources.
-    Clippy {
-        /// Flags that are passed through to `cargo clippy`.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Runs just `cargo <flags>` with the Miri-specific environment variables.
-    /// Mainly meant to be invoked by rust-analyzer.
-    Cargo {
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
-    /// variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
-    /// many different seeds.
-    ManySeeds {
-        /// Starting seed.
-        #[clap(long, env("MIRI_SEED_START"), default_value_t = 0)]
-        seed_start: u64,
-        #[clap(long, env("MIRI_SEEDS"), default_value_t = 256)]
-        /// Amount of seeds to try.
-        seeds: u64,
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        command: Vec<OsString>,
-    },
-    /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
-    Bench {
-        /// List of benchmarks to run. By default all benchmarks are run.
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        benches: Vec<OsString>,
-    },
-    /// 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 {
-        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
-        flags: Vec<OsString>,
-    },
-    /// 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 },
-}
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index 121b5067804..33e407a65da 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -1,307 +1,100 @@
-use std::collections::BTreeMap;
-
-use std::ffi::{OsStr, OsString};
-use std::path::{Path, PathBuf};
+use std::env;
+use std::ffi::OsString;
+use std::io::Write;
+use std::ops::Not;
 
 use anyhow::{anyhow, bail, Context, Result};
-use dunce::canonicalize;
 use path_macro::path;
-use xshell::{cmd, Shell};
-
 use walkdir::WalkDir;
+use xshell::cmd;
 
-use crate::arg::Subcommands;
+use crate::util::*;
+use crate::Command;
 
 /// Used for rustc syncs.
 const JOSH_FILTER: &str =
     ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri";
 
-fn detect_miri_dir() -> std::io::Result<PathBuf> {
-    const MIRI_SCRIPT_ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR");
-    Ok(canonicalize(MIRI_SCRIPT_ROOT_DIR)?.parent().unwrap().into())
-}
-
-/// Queries an active toolchain for `dir` via `rustup`.
-fn get_active_toolchain(dir: &Path) -> Result<String> {
-    let sh = Shell::new()?;
-    sh.change_dir(dir);
-    let stdout = cmd!(sh, "rustup show active-toolchain").read()?;
-    Ok(stdout.split_whitespace().next().context("Could not obtain active Rust toolchain")?.into())
-}
-
-#[derive(Clone, Debug)]
-pub(super) struct MiriRunner<'a> {
-    /// miri_dir is the root of the miri repository checkout we are working in.
-    miri_dir: PathBuf,
-    /// active_toolchain is passed as `+toolchain` argument to cargo/rustc invocations.
-    active_toolchain: String,
-    cargo_extra_flags: Vec<String>,
-    command: &'a super::Subcommands,
-    /// Environment variables passed to child processes.
-    env: BTreeMap<OsString, OsString>,
-    /// Additional variables used by environment-altering commands.
-    /// These should be accessed by corresponding methods (e.g. `sysroot()`) and not directly.
-    sysroot: Option<PathBuf>,
-}
-
-fn shell_with_parent_env() -> Result<Shell> {
-    let sh = Shell::new()?;
-    // xshell does not propagate parent's env variables by default.
-    for (k, v) in std::env::vars_os() {
-        sh.set_var(k, v);
-    }
-    Ok(sh)
-}
-
-impl MiriRunner<'_> {
-    pub(super) fn exec(command: &super::Subcommands) -> Result<()> {
-        Self::exec_inner(command, true)
-    }
-    fn exec_inner(command: &super::Subcommands, run_auto_things: bool) -> Result<()> {
-        let miri_dir = detect_miri_dir()?;
-        let active_toolchain = get_active_toolchain(&miri_dir)?;
-        let config = command.get_config(&miri_dir);
-        // CARGO_EXTRA_FLAGS do not have to be a valid UTF-8, but that's what shell_words' expects.
-        let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
-        let cargo_extra_flags = shell_words::split(&cargo_extra_flags)?;
-        let env = BTreeMap::new();
-
-        let mut runner = MiriRunner {
-            miri_dir,
-            active_toolchain,
-            command,
-            env,
-            cargo_extra_flags,
-            sysroot: None,
-        };
-        if let Some(config) = config {
-            // Run the auto-things.
-            if run_auto_things {
-                if config.toolchain {
-                    // Run this first, so that the toolchain doesn't change after
-                    // other code has run.
-                    let command = Subcommands::Toolchain { flags: vec![] };
-                    Self::exec_inner(&command, false)?;
-                    // Let's make sure to actually use that toolchain, too.
-                    runner.active_toolchain = "miri".to_owned();
-                }
-                if config.fmt {
-                    let command = Subcommands::Fmt { flags: vec![] };
-                    Self::exec_inner(&command, false)?;
-                }
-                if config.clippy {
-                    let command = Subcommands::Clippy {
-                        flags: ["--", "-D", "warnings"].into_iter().map(OsString::from).collect(),
-                    };
-                    Self::exec_inner(&command, false)?;
-                }
-            }
-
-            // Prepare the environment
-            // Determine some toolchain properties
-            let libdir = runner.libdir()?;
-            if !libdir.exists() {
-                println!("Something went wrong determining the library dir.");
-                println!("I got {} but that does not exist.", libdir.display());
-                println!("Please report a bug at https://github.com/rust-lang/miri/issues.");
-                std::process::exit(2);
-            }
-            // Share target dir between `miri` and `cargo-miri`.
-            let target_dir = std::env::var_os("CARGO_TARGET_DIR")
-                .filter(|val| !val.is_empty())
-                .unwrap_or_else(|| {
-                    let target_dir = path!(runner.miri_dir / "target");
-                    target_dir.into()
-                });
-            runner.set_env("CARGO_TARGET_DIR", target_dir);
-
-            // We configure dev builds to not be unusably slow.
-            let devel_opt_level = std::env::var_os("CARGO_PROFILE_DEV_OPT_LEVEL")
-                .filter(|val| !val.is_empty())
-                .unwrap_or_else(|| "2".into());
-            runner.set_env("CARGO_PROFILE_DEV_OPT_LEVEL", devel_opt_level);
-            let rustflags = {
-                let env = std::env::var_os("RUSTFLAGS");
-                let mut flags_with_warnings = OsString::from(
-                    "-Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros ",
-                );
-                if let Some(value) = env {
-                    flags_with_warnings.push(value);
-                }
-                // We set the rpath so that Miri finds the private rustc libraries it needs.
-                let mut flags_with_compiler_settings = OsString::from("-C link-args=-Wl,-rpath,");
-                flags_with_compiler_settings.push(&libdir);
-                flags_with_compiler_settings.push(flags_with_warnings);
-                flags_with_compiler_settings
-            };
-            runner.set_env("RUSTFLAGS", rustflags);
-        }
-        runner.execute()
-    }
-    fn execute(&mut self) -> Result<()> {
-        // Run command.
-        match self.command {
-            Subcommands::Install { flags } => self.install(flags),
-            Subcommands::Build { flags } => self.build(flags),
-            Subcommands::Check { flags } => self.check(flags),
-            Subcommands::Test { bless, flags } => self.test(*bless, flags),
-            Subcommands::Run { dep, flags } => self.run(*dep, flags),
-            Subcommands::Fmt { flags } => self.fmt(flags),
-            Subcommands::Clippy { flags } => self.clippy(flags),
-            Subcommands::Cargo { flags } => self.cargo(flags),
-            Subcommands::ManySeeds { command, seed_start, seeds } =>
-                self.many_seeds(command, *seed_start, *seeds),
-            Subcommands::Bench { benches } => self.bench(benches),
-            Subcommands::Toolchain { flags } => self.toolchain(flags),
-            Subcommands::RustcPull { commit } => self.rustc_pull(commit.clone()),
-            Subcommands::RustcPush { github_user, branch } => self.rustc_push(github_user, branch),
-        }
-    }
-
-    fn set_env(
-        &mut self,
-        key: impl Into<OsString>,
-        value: impl Into<OsString>,
-    ) -> Option<OsString> {
-        self.env.insert(key.into(), value.into())
-    }
-
-    /// Prepare and set MIRI_SYSROOT. Respects `MIRI_TEST_TARGET` and takes into account
-    /// locally built vs. distributed rustc.
-    fn find_miri_sysroot(&mut self) -> Result<()> {
-        let current_sysroot = std::env::var_os("MIRI_SYSROOT").unwrap_or_default();
-
-        if !current_sysroot.is_empty() {
+impl MiriEnv {
+    fn build_miri_sysroot(&mut self) -> Result<()> {
+        if self.sh.var("MIRI_SYSROOT").is_ok() {
             // Sysroot already set, use that.
-            let current_value = self.set_env("MIRI_SYSROOT", &current_sysroot);
-            assert!(current_value.is_none() || current_value.unwrap() == current_sysroot);
             return Ok(());
         }
-        // We need to build a sysroot.
-        let target = std::env::var_os("MIRI_TEST_TARGET").filter(|target| !target.is_empty());
-        let sysroot = self.build_miri_sysroot(target.as_deref())?;
-        self.set_env("MIRI_SYSROOT", sysroot);
+        let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
+        let Self { toolchain, cargo_extra_flags, .. } = &self;
+        let target = &match self.sh.var("MIRI_TEST_TARGET") {
+            Ok(target) => vec!["--target".into(), target],
+            Err(_) => vec![],
+        };
+        let output = cmd!(self.sh,
+            "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} --
+             miri setup --print-sysroot {target...}"
+        ).read();
+        let Ok(output) = output else {
+            // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error.
+            cmd!(
+                self.sh,
+                "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} --
+                miri setup {target...}"
+            )
+            .run()
+            .with_context(|| "`cargo miri setup` failed")?;
+            panic!("`cargo miri setup` didn't fail again the 2nd time?");
+        };
+        self.sh.set_var("MIRI_SYSROOT", output);
         Ok(())
     }
+}
 
-    /// Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`.
-    fn build_miri_sysroot(&self, target: Option<&OsStr>) -> Result<String> {
-        let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
-        let Self { active_toolchain, cargo_extra_flags, .. } = &self;
-        let target_prefix: Option<&OsStr> = target.map(|_| "--target".as_ref());
-        let sh = self.shell()?;
-        let output = cmd!(sh, "cargo +{active_toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} -- miri setup --print-sysroot {target_prefix...} {target...}").read();
-        if output.is_err() {
-            // Run it again (without `--print-sysroot`) so the user can see the error.
-            cmd!(sh, "cargo +{active_toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} -- miri setup {target_prefix...} {target...}").run().with_context(|| "`cargo miri setup` failed")?;
+impl Command {
+    fn auto_actions() -> Result<()> {
+        let miri_dir = miri_dir()?;
+        let auto_everything = path!(miri_dir / ".auto_everything").exists();
+        let auto_toolchain = auto_everything || path!(miri_dir / ".auto-toolchain").exists();
+        let auto_fmt = auto_everything || path!(miri_dir / ".auto-fmt").exists();
+        let auto_clippy = auto_everything || path!(miri_dir / ".auto-clippy").exists();
+
+        // `toolchain` goes first as it could affect the others
+        if auto_toolchain {
+            Self::toolchain(vec![])?;
         }
-
-        Ok(output?)
-    }
-    fn build_package(
-        // Path to Cargo.toml file of a package to build.
-        path: &OsStr,
-        toolchain: impl AsRef<OsStr>,
-        extra_flags: &[String],
-        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
-    ) -> Result<()> {
-        let sh = Shell::new()?;
-        cmd!(sh, "cargo +{toolchain} build {extra_flags...} --manifest-path {path} {args...}")
-            .run()?;
-        Ok(())
-    }
-    fn shell(&self) -> Result<Shell> {
-        let sh = shell_with_parent_env()?;
-        for (k, v) in &self.env {
-            sh.set_var(k, v);
+        if auto_fmt {
+            Self::fmt(vec![])?;
         }
-
-        Ok(sh)
-    }
-
-    fn libdir(&self) -> Result<PathBuf> {
-        let sh = shell_with_parent_env()?;
-        let toolchain = &self.active_toolchain;
-        let target_output = cmd!(sh, "rustc +{toolchain} --version --verbose").read()?;
-        let rustc_meta = rustc_version::version_meta_for(&target_output)?;
-        let target = rustc_meta.host;
-
-        let sysroot = cmd!(sh, "rustc +{toolchain} --print sysroot").read()?;
-
-        let sysroot = PathBuf::from(sysroot);
-        let libdir = path!(sysroot / "lib" / "rustlib" / target / "lib");
-        Ok(libdir)
-    }
-    fn sysroot(&mut self) -> Result<PathBuf> {
-        if let Some(sysroot) = self.sysroot.as_ref() {
-            Ok(sysroot.clone())
-        } else {
-            let sh = shell_with_parent_env()?;
-            let toolchain = &self.active_toolchain;
-
-            let sysroot: PathBuf = cmd!(sh, "rustc +{toolchain} --print sysroot").read()?.into();
-            self.sysroot = Some(sysroot.clone());
-            Ok(sysroot)
+        if auto_clippy {
+            Self::clippy(vec![])?;
         }
-    }
-    fn install_to_dir(
-        &mut self,
-        sh: &Shell,
-        path: PathBuf,
-        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
-    ) -> Result<()> {
-        let sysroot = self.sysroot()?;
-        let toolchain = &self.active_toolchain;
-        let extra_flags = &self.cargo_extra_flags;
-        // "--locked" to respect the Cargo.lock file if it exists.
-        // Install binaries to the miri toolchain's sysroot so they do not interact with other toolchains.
-        cmd!(sh, "cargo +{toolchain} install {extra_flags...} --path {path} --force --root {sysroot} {args...}").run()?;
+
         Ok(())
     }
-}
 
-impl MiriRunner<'_> {
-    fn bench(&self, benches: &[OsString]) -> Result<()> {
-        // The hyperfine to use
-        let hyperfine = std::env::var("HYPERFINE");
-        let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
-        let hyperfine = shell_words::split(hyperfine).unwrap();
-        let Some((program_name, args)) = hyperfine.split_first() else {
-            bail!("Expected HYPERFINE environment variable to be non-empty");
-        };
-        // Make sure we have an up-to-date Miri installed
-        Self::exec_inner(&Subcommands::Install { flags: vec![] }, false)?;
-        let benches_dir = path!(self.miri_dir / "bench-cargo-miri");
-        let benches = if benches.is_empty() {
-            std::fs::read_dir(&benches_dir)?
-                .filter_map(|path| {
-                    path.ok()
-                        .filter(|dir| dir.file_type().map(|t| t.is_dir()).unwrap_or(false))
-                        .map(|p| p.file_name())
-                })
-                .collect()
-        } else {
-            benches.to_owned()
-        };
-        let sh = shell_with_parent_env()?;
-        let toolchain = &self.active_toolchain;
-        // Run the requested benchmarks
-        for bench in benches {
-            let current_bench_dir = path!(benches_dir / bench / "Cargo.toml");
-            cmd!(
-                sh,
-                "{program_name} {args...} 'cargo +'{toolchain}' miri run --manifest-path \"'{current_bench_dir}'\"'"
-            )
-            .run()?;
+    pub fn exec(self) -> Result<()> {
+        match self {
+            Command::Install { flags } => Self::install(flags),
+            Command::Build { flags } => Self::build(flags),
+            Command::Check { flags } => Self::check(flags),
+            Command::Test { bless, flags } => Self::test(bless, flags),
+            Command::Run { dep, flags } => Self::run(dep, flags),
+            Command::Fmt { flags } => Self::fmt(flags),
+            Command::Clippy { flags } => Self::clippy(flags),
+            Command::Cargo { flags } => Self::cargo(flags),
+            Command::ManySeeds { command, seed_start, seeds } =>
+                Self::many_seeds(command, seed_start, seeds),
+            Command::Bench { benches } => Self::bench(benches),
+            Command::Toolchain { flags } => Self::toolchain(flags),
+            Command::RustcPull { commit } => Self::rustc_pull(commit.clone()),
+            Command::RustcPush { rustc_git, github_user, branch } =>
+                Self::rustc_push(rustc_git, github_user, branch),
         }
-        Ok(())
     }
 
-    fn toolchain(&self, flags: &[OsString]) -> Result<()> {
+    fn toolchain(flags: Vec<OsString>) -> Result<()> {
         // Make sure rustup-toolchain-install-master is installed.
-        which::which("rustup-toolchain-install-master").context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
-        let sh = shell_with_parent_env()?;
-        sh.change_dir(&self.miri_dir);
+        which::which("rustup-toolchain-install-master")
+            .context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
+        let sh = shell()?;
+        sh.change_dir(miri_dir()?);
         let new_commit = Some(sh.read_file("rust-version")?.trim().to_owned());
         let current_commit = {
             let rustc_info = cmd!(sh, "rustc +miri --version -v").read();
@@ -318,8 +111,9 @@ impl MiriRunner<'_> {
         };
         // Check if we already are at that commit.
         if current_commit == new_commit {
-            println!("miri toolchain is already at commit {}.", current_commit.unwrap());
-            cmd!(sh, "rustup override set miri").run()?;
+            if active_toolchain()? != "miri" {
+                cmd!(sh, "rustup override set miri").run()?;
+            }
             return Ok(());
         }
         // Install and setup new toolchain.
@@ -337,10 +131,10 @@ impl MiriRunner<'_> {
         Ok(())
     }
 
-    fn rustc_pull(&self, commit: Option<String>) -> Result<()> {
-        let sh = shell_with_parent_env()?;
-        sh.change_dir(&self.miri_dir);
-        let commit: String = commit.map(Result::Ok).unwrap_or_else(|| {
+    fn rustc_pull(commit: Option<String>) -> Result<()> {
+        let sh = shell()?;
+        sh.change_dir(miri_dir()?);
+        let commit = commit.map(Result::Ok).unwrap_or_else(|| {
             let rust_repo_head =
                 cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?;
             rust_repo_head
@@ -349,85 +143,109 @@ impl MiriRunner<'_> {
                 .map(|front| front.trim().to_owned())
                 .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote."))
         })?;
+        // Make sure the repo is clean.
+        if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
+            bail!("working directory must be clean before running `./miri rustc-pull`");
+        }
+
         // Update rust-version file. As a separate commit, since making it part of
         // the merge has confused the heck out of josh in the past.
-        sh.write_file(path!(self.miri_dir / "rust-version"), &commit)?;
+        // We pass `--no-verify` to avoid running git hooks like `./miri fmt` that could in turn
+        // trigger auto-actions.
+        sh.write_file("rust-version", &commit)?;
         const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc";
-        cmd!(sh, "git commit rust-version -m {PREPARING_COMMIT_MESSAGE}")
+        cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}")
             .run()
             .context("FAILED to commit rust-version file, something went wrong")?;
-        // Fetch given rustc commit and note down which one that was
+
+        // Fetch given rustc commit.
         cmd!(sh, "git fetch http://localhost:8000/rust-lang/rust.git@{commit}{JOSH_FILTER}.git")
             .run()
-            .context("FAILED to fetch new commits, something went wrong")?;
+            .map_err(|e| {
+                // Try to un-do the previous `git commit`, to leave the repo in the state we found it it.
+                cmd!(sh, "git reset --hard HEAD^")
+                    .run()
+                    .expect("FAILED to clean up again after failed `git fetch`, sorry for that");
+                e
+            })
+            .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?;
+
+        // Merge the fetched commit.
         const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc";
-        cmd!(sh, "git merge FETCH_HEAD --no-ff -m {MERGE_COMMIT_MESSAGE}")
+        cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}")
             .run()
             .context("FAILED to merge new commits, something went wrong")?;
         Ok(())
     }
 
-    fn rustc_push(&self, github_user: &str, branch: &str) -> Result<()> {
-        let rustc_git = std::env::var_os("RUSTC_GIT");
-        let working_directory = if let Some(rustc_git) = rustc_git {
-            rustc_git
-        } else {
+    fn rustc_push(rustc_git: Option<String>, github_user: String, branch: String) -> Result<()> {
+        let sh = shell()?;
+        sh.change_dir(miri_dir()?);
+        let base = sh.read_file("rust-version")?.trim().to_owned();
+        // Make sure the repo is clean.
+        if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() {
+            bail!("working directory must be clean before running `./miri rustc-push`");
+        }
+
+        // Find a repo we can do our preparation in.
+        if let Some(rustc_git) = rustc_git {
             // If rustc_git is `Some`, we'll use an existing fork for the branch updates.
+            sh.change_dir(rustc_git);
+        } else {
             // Otherwise, do this in the local Miri repo.
             println!(
                 "This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB."
             );
-            println!(
-                "To avoid that, abort now and set the RUSTC_GIT environment variable to an existing rustc checkout. Proceed? [y/N] "
+            print!(
+                "To avoid that, abort now and set the `--rustc-git` flag to an existing rustc checkout. Proceed? [y/N] "
             );
+            std::io::stdout().flush()?;
             let mut answer = String::new();
             std::io::stdin().read_line(&mut answer)?;
             if answer.trim().to_lowercase() != "y" {
                 std::process::exit(1);
             }
-            self.miri_dir.clone().into()
         };
         // Prepare the branch. Pushing works much better if we use as base exactly
         // the commit that we pulled from last time, so we use the `rust-version`
         // file as a good approximation of that.
-        let rust_version_path = path!(self.miri_dir / "rust-version");
-        let base = std::fs::read_to_string(rust_version_path)?.trim().to_owned();
-        println!("Preparing {github_user}/rust (base: {base})...)");
-        let sh = shell_with_parent_env()?;
-        sh.change_dir(working_directory);
-
-        if cmd!(sh, "git fetch https://github.com/{github_user}").read().is_ok() {
+        println!("Preparing {github_user}/rust (base: {base})...");
+        if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}")
+            .ignore_stderr()
+            .read()
+            .is_ok()
+        {
             println!(
-                "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}'. Please delete it and try again."
+                "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again."
             );
             std::process::exit(1);
         }
-
         cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?;
-
         cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}")
             .run()?;
         println!();
+
         // Do the actual push.
-        sh.change_dir(&self.miri_dir);
+        sh.change_dir(miri_dir()?);
         println!("Pushing miri changes...");
         cmd!(
             sh,
             "git push http://localhost:8000/{github_user}/rust.git{JOSH_FILTER}.git HEAD:{branch}"
         )
         .run()?;
-        // Do a round-trip check to make sure the push worked as expected.
         println!();
+
+        // Do a round-trip check to make sure the push worked as expected.
         cmd!(
             sh,
             "git fetch http://localhost:8000/{github_user}/rust.git{JOSH_FILTER}.git {branch}"
         )
+        .ignore_stderr()
         .read()?;
         let head = cmd!(sh, "git rev-parse HEAD").read()?;
         let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?;
         if head != fetch_head {
-            println!("ERROR: Josh created a non-roundtrip push! Do NOT merge this into rustc!");
-            std::process::exit(1);
+            bail!("Josh created a non-roundtrip push! Do NOT merge this into rustc!");
         }
         println!(
             "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"
@@ -436,194 +254,196 @@ impl MiriRunner<'_> {
         Ok(())
     }
 
-    fn install(&mut self, flags: &[OsString]) -> Result<()> {
-        let sh = self.shell()?;
-        self.install_to_dir(&sh, self.miri_dir.clone(), flags)?;
-        let cargo_miri_dir = path!(self.miri_dir / "cargo-miri");
-        self.install_to_dir(&sh, cargo_miri_dir, flags)?;
+    fn many_seeds(command: Vec<OsString>, seed_start: u64, seed_count: u64) -> Result<()> {
+        let seed_end = seed_start + seed_count;
+        let Some((command_name, trailing_args)) = command.split_first() else {
+            bail!("expected many-seeds command to be non-empty");
+        };
+        let sh = shell()?;
+        for seed in seed_start..seed_end {
+            println!("Trying seed: {seed}");
+            let mut miriflags = env::var_os("MIRIFLAGS").unwrap_or_default();
+            miriflags.push(format!(" -Zlayout-seed={seed} -Zmiri-seed={seed}"));
+            let status = cmd!(sh, "{command_name} {trailing_args...}")
+                .env("MIRIFLAGS", miriflags)
+                .quiet()
+                .run();
+            if status.is_err() {
+                println!("Failing seed: {seed}");
+                break;
+            }
+        }
         Ok(())
     }
 
-    fn build(&self, flags: &[OsString]) -> Result<()> {
-        // Build, and let caller control flags.
-        let miri_manifest = path!(self.miri_dir / "Cargo.toml");
-        let cargo_miri_manifest = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
-        Self::build_package(
-            miri_manifest.as_ref(),
-            &self.active_toolchain,
-            &self.cargo_extra_flags,
-            flags,
-        )?;
-        Self::build_package(
-            cargo_miri_manifest.as_ref(),
-            &self.active_toolchain,
-            &self.cargo_extra_flags,
-            flags,
-        )?;
+    fn bench(benches: Vec<OsString>) -> Result<()> {
+        // The hyperfine to use
+        let hyperfine = env::var("HYPERFINE");
+        let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
+        let hyperfine = shell_words::split(hyperfine)?;
+        let Some((program_name, args)) = hyperfine.split_first() else {
+            bail!("expected HYPERFINE environment variable to be non-empty");
+        };
+        // Make sure we have an up-to-date Miri installed and selected the right toolchain.
+        Self::install(vec![])?;
+
+        let sh = shell()?;
+        sh.change_dir(miri_dir()?);
+        let benches_dir = "bench-cargo-miri";
+        let benches = if benches.is_empty() {
+            sh.read_dir(benches_dir)?
+                .into_iter()
+                .filter(|path| path.is_dir())
+                .map(Into::into)
+                .collect()
+        } else {
+            benches.to_owned()
+        };
+        // Run the requested benchmarks
+        for bench in benches {
+            let current_bench = path!(benches_dir / bench / "Cargo.toml");
+            // We don't attempt to escape `current_bench`, but we wrap it in quotes.
+            // That seems to make Windows CI happy.
+            cmd!(
+                sh,
+                "{program_name} {args...} 'cargo miri run --manifest-path \"'{current_bench}'\"'"
+            )
+            .run()?;
+        }
         Ok(())
     }
 
-    fn check(&self, flags: &[OsString]) -> Result<()> {
-        fn check_package(
-            // Path to Cargo.toml file of a package to check.
-            path: &OsStr,
-            toolchain: impl AsRef<OsStr>,
-            extra_flags: &[String],
-            all_targets: bool,
-            args: impl IntoIterator<Item = impl AsRef<OsStr>>,
-        ) -> Result<()> {
-            let all_targets: Option<&OsStr> = all_targets.then_some("--all-targets".as_ref());
-            let sh = Shell::new()?;
-            cmd!(sh, "cargo +{toolchain} check {extra_flags...} --manifest-path {path} {all_targets...} {args...}").run()?;
-            Ok(())
-        }
-        // Check, and let caller control flags.
-        let miri_manifest = path!(self.miri_dir / "Cargo.toml");
-        let cargo_miri_manifest = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
-        check_package(
-            miri_manifest.as_ref(),
-            &self.active_toolchain,
-            &self.cargo_extra_flags,
-            true,
-            flags,
-        )?;
-        check_package(
-            cargo_miri_manifest.as_ref(),
-            &self.active_toolchain,
-            &self.cargo_extra_flags,
-            false,
-            flags,
-        )?;
+    fn install(flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let e = MiriEnv::new()?;
+        e.install_to_sysroot(e.miri_dir.clone(), &flags)?;
+        e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?;
+        Ok(())
+    }
+
+    fn build(flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let e = MiriEnv::new()?;
+        e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?;
+        e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?;
+        Ok(())
+    }
+
+    fn check(flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let e = MiriEnv::new()?;
+        e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?;
+        e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
+        Ok(())
+    }
+
+    fn clippy(flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let e = MiriEnv::new()?;
+        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 / "miri-script" / "Cargo.toml"), &flags)?;
+        Ok(())
+    }
+
+    fn cargo(flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let e = MiriEnv::new()?;
+        let toolchain = &e.toolchain;
+        // We carefully kept the working dir intact, so this will run cargo *on the workspace in the
+        // current working dir*, not on the main Miri workspace. That is exactly what RA needs.
+        cmd!(e.sh, "cargo +{toolchain} {flags...}").run()?;
         Ok(())
     }
 
-    fn test(&mut self, bless: bool, flags: &[OsString]) -> Result<()> {
-        let miri_manifest = path!(self.miri_dir / "Cargo.toml");
-        // First build and get a sysroot.
-        Self::build_package(
-            miri_manifest.as_ref(),
-            &self.active_toolchain,
-            &self.cargo_extra_flags,
-            std::iter::empty::<OsString>(),
-        )?;
-        self.find_miri_sysroot()?;
-        let extra_flags = &self.cargo_extra_flags;
+    fn test(bless: bool, flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let mut e = MiriEnv::new()?;
+        // First build, and get a sysroot.
+        e.build(path!(e.miri_dir / "Cargo.toml"), &[], /* quiet */ true)?;
+        e.build_miri_sysroot()?;
+
         // Then test, and let caller control flags.
         // Only in root project as `cargo-miri` has no tests.
-        let sh = self.shell()?;
         if bless {
-            sh.set_var("MIRI_BLESS", "Gesundheit");
+            e.sh.set_var("RUSTC_BLESS", "Gesundheit");
         }
-        let toolchain: &OsStr = self.active_toolchain.as_ref();
-        cmd!(
-            sh,
-            "cargo +{toolchain} test {extra_flags...} --manifest-path {miri_manifest} -- {flags...}"
-        )
-        .run()?;
+        e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?;
         Ok(())
     }
 
-    fn run(&mut self, dep: bool, flags: &[OsString]) -> Result<()> {
-        use itertools::Itertools;
+    fn run(dep: bool, flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let mut e = MiriEnv::new()?;
         // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so
         // that we set the MIRI_SYSROOT up the right way.
+        use itertools::Itertools;
         let target = flags.iter().tuple_windows().find(|(first, _)| first == &"--target");
         if let Some((_, target)) = target {
             // Found it!
-            self.set_env("MIRI_TEST_TARGET", target);
-        } else if let Some(var) =
-            std::env::var_os("MIRI_TEST_TARGET").filter(|target| !target.is_empty())
-        {
+            e.sh.set_var("MIRI_TEST_TARGET", target);
+        } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") {
             // Make sure miri actually uses this target.
-            let entry = self.env.entry("MIRIFLAGS".into()).or_default();
-            entry.push(" --target ");
-            entry.push(var);
+            let miriflags = e.sh.var("MIRIFLAGS").unwrap_or_default();
+            e.sh.set_var("MIRIFLAGS", format!("{miriflags} --target {target}"));
         }
-        // First build and get a sysroot.
-        let miri_manifest = path!(self.miri_dir / "Cargo.toml");
-        Self::build_package(
-            miri_manifest.as_ref(),
-            &self.active_toolchain,
-            &self.cargo_extra_flags,
-            std::iter::empty::<OsString>(),
-        )?;
-        self.find_miri_sysroot()?;
+        // First build, and get a sysroot.
+        let miri_manifest = path!(e.miri_dir / "Cargo.toml");
+        e.build(&miri_manifest, &[], /* quiet */ true)?;
+        e.build_miri_sysroot()?;
+
         // Then run the actual command.
-        let miri_flags = self.env.get(&OsString::from("MIRIFLAGS")).cloned().unwrap_or_default();
-        let miri_flags: &OsStr = miri_flags.as_ref();
-        let extra_flags = &self.cargo_extra_flags;
-        let sh = self.shell()?;
-        let toolchain: &OsStr = self.active_toolchain.as_ref();
+        let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default();
+        let miri_flags = flagsplit(&miri_flags);
+        let toolchain = &e.toolchain;
+        let extra_flags = &e.cargo_extra_flags;
         if dep {
             cmd!(
-                sh,
-                "cargo +{toolchain} --quiet test --test compiletest {extra_flags...} --manifest-path {miri_manifest} -- --miri-run-dep-mode {miri_flags} {flags...}"
-            ).run()?;
+                e.sh,
+                "cargo +{toolchain} --quiet test --test compiletest {extra_flags...} --manifest-path {miri_manifest} -- --miri-run-dep-mode {miri_flags...} {flags...}"
+            ).quiet().run()?;
         } else {
             cmd!(
-                sh,
-                "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags} {flags...}"
-            ).run()?;
+                e.sh,
+                "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}"
+            ).quiet().run()?;
         }
         Ok(())
     }
 
-    fn fmt(&self, flags: &[OsString]) -> Result<()> {
-        let toolchain = &self.active_toolchain;
-        let config_path = path!(self.miri_dir / "rustfmt.toml");
-        let sh = self.shell()?;
-        for item in WalkDir::new(&self.miri_dir).into_iter().filter_entry(|entry| {
-            let name: String = entry.file_name().to_string_lossy().into();
+    fn fmt(flags: Vec<OsString>) -> Result<()> {
+        Self::auto_actions()?;
+        let e = MiriEnv::new()?;
+        let toolchain = &e.toolchain;
+        let config_path = path!(e.miri_dir / "rustfmt.toml");
+
+        let mut cmd = cmd!(
+            e.sh,
+            "rustfmt +{toolchain} --edition=2021 --config-path {config_path} {flags...}"
+        );
+        eprintln!("$ {cmd} ...");
+
+        // Add all the filenames to the command.
+        // FIXME: `rustfmt` will follow the `mod` statements in these files, so we get a bunch of
+        // duplicate diffs.
+        for item in WalkDir::new(&e.miri_dir).into_iter().filter_entry(|entry| {
+            let name = entry.file_name().to_string_lossy();
             let ty = entry.file_type();
             if ty.is_file() {
                 name.ends_with(".rs")
             } else {
-                // dir or symlink
-                &name != "target"
+                // dir or symlink. skip `target` and `.git`.
+                &name != "target" && &name != ".git"
             }
         }) {
-            let item = item.unwrap(); // Should never panic as we've already filtered out failed entries.
+            let item = item?;
             if item.file_type().is_file() {
-                let path = item.path();
-                cmd!(sh, "rustfmt +{toolchain} --edition=2021 --config-path {config_path} {flags...} {path}").quiet().run()?;
+                cmd = cmd.arg(item.into_path());
             }
         }
-        Ok(())
-    }
-
-    fn clippy(&self, flags: &[OsString]) -> Result<()> {
-        let toolchain_modifier = &self.active_toolchain;
-        let extra_flags = &self.cargo_extra_flags;
-        let miri_manifest = path!(self.miri_dir / "Cargo.toml");
-        let sh = self.shell()?;
-        cmd!(sh, "cargo +{toolchain_modifier} clippy {extra_flags...} --manifest-path {miri_manifest} --all-targets -- {flags...}").run()?;
-        Ok(())
-    }
 
-    fn cargo(&self, flags: &[OsString]) -> Result<()> {
-        // We carefully kept the working dir intact, so this will run cargo *on the workspace in the
-        // current working dir*, not on the main Miri workspace. That is exactly what RA needs.
-        let toolchain_modifier = &self.active_toolchain;
-        let sh = self.shell()?;
-        cmd!(sh, "cargo +{toolchain_modifier} {flags...}").run()?;
-        Ok(())
-    }
-    fn many_seeds(&self, command: &[OsString], seed_start: u64, seed_count: u64) -> Result<()> {
-        let seed_end = seed_start + seed_count;
-        assert!(!command.is_empty());
-        let (command_name, trailing_args) = command.split_first().unwrap();
-        let sh = shell_with_parent_env()?;
-        for seed in seed_start..seed_end {
-            println!("Trying seed: {seed}");
-            let mut miriflags = std::env::var_os("MIRIFLAGS").unwrap_or_default();
-            miriflags.push(format!(" -Zlayout-seed={seed} -Zmiri-seed={seed}"));
-            let status =
-                cmd!(sh, "{command_name} {trailing_args...}").env("MIRIFLAGS", miriflags).run();
-            if status.is_err() {
-                println!("Failing seed: {seed}");
-                break;
-            }
-        }
+        // We want our own error message, repeating the command is too much.
+        cmd.quiet().run().map_err(|_| anyhow!("`rustfmt` failed"))?;
         Ok(())
     }
 }
diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs
index dbee923d48f..ce00de5ac54 100644
--- a/src/tools/miri/miri-script/src/main.rs
+++ b/src/tools/miri/miri-script/src/main.rs
@@ -1,58 +1,120 @@
-pub(crate) mod arg;
 mod commands;
+mod util;
 
-use std::path::Path;
+use std::ffi::OsString;
 
 use anyhow::Result;
-use clap::Parser;
-use path_macro::path;
+use clap::{Parser, Subcommand};
 
-use arg::Subcommands;
-
-struct AutoConfig {
-    toolchain: bool,
-    fmt: bool,
-    clippy: bool,
+#[derive(Parser, Clone, Debug)]
+#[command(author, about, long_about = None)]
+pub struct Cli {
+    #[command(subcommand)]
+    pub command: Command,
 }
 
-impl Subcommands {
-    fn run_auto_things(&self) -> bool {
-        use Subcommands::*;
-        match self {
-            // Early commands, that don't do auto-things and don't want the environment-altering things happening below.
-            Toolchain { .. }
-            | RustcPull { .. }
-            | RustcPush { .. }
-            | ManySeeds { .. }
-            | Bench { .. } => false,
-            Install { .. }
-            | Check { .. }
-            | Build { .. }
-            | Test { .. }
-            | Run { .. }
-            | Fmt { .. }
-            | Clippy { .. }
-            | Cargo { .. } => true,
-        }
-    }
-    fn get_config(&self, miri_dir: &Path) -> Option<AutoConfig> {
-        let skip_auto_ops = std::env::var_os("MIRI_AUTO_OPS").is_some();
-        if !self.run_auto_things() {
-            return None;
-        }
-        if skip_auto_ops {
-            return Some(AutoConfig { toolchain: false, fmt: false, clippy: false });
-        }
-
-        let auto_everything = path!(miri_dir / ".auto_everything").exists();
-        let toolchain = auto_everything || path!(miri_dir / ".auto-toolchain").exists();
-        let fmt = auto_everything || path!(miri_dir / ".auto-fmt").exists();
-        let clippy = auto_everything || path!(miri_dir / ".auto-clippy").exists();
-        Some(AutoConfig { toolchain, fmt, clippy })
-    }
+#[derive(Subcommand, 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`.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Just build miri.
+    Build {
+        /// Flags that are passed through to `cargo build`.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Just check miri.
+    Check {
+        /// Flags that are passed through to `cargo check`.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Build miri, set up a sysroot and then run the test suite.
+    Test {
+        #[arg(long, default_value_t = false)]
+        bless: bool,
+        /// Flags that are passed through to `cargo test`.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Build miri, set up a sysroot and then run the driver with the given <flags>.
+    /// (Also respects MIRIFLAGS environment variable.)
+    Run {
+        #[arg(long, default_value_t = false)]
+        dep: bool,
+        /// Flags that are passed through to `miri`
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Format all sources and tests.
+    Fmt {
+        /// Flags that are passed through to `rustfmt`.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Runs clippy on all sources.
+    Clippy {
+        /// Flags that are passed through to `cargo clippy`.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Runs just `cargo <flags>` with the Miri-specific environment variables.
+    /// Mainly meant to be invoked by rust-analyzer.
+    Cargo {
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// Runs <command> over and over again with different seeds for Miri. The MIRIFLAGS
+    /// variable is set to its original value appended with ` -Zmiri-seed=$SEED` for
+    /// many different seeds.
+    ManySeeds {
+        /// Starting seed.
+        #[arg(long, env = "MIRI_SEED_START", default_value_t = 0)]
+        seed_start: u64,
+        /// Amount of seeds to try.
+        #[arg(long, env = "MIRI_SEEDS", default_value_t = 256)]
+        seeds: u64,
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        command: Vec<OsString>,
+    },
+    /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
+    Bench {
+        /// List of benchmarks to run. By default all benchmarks are run.
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        benches: Vec<OsString>,
+    },
+    /// 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 {
+        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
+        flags: Vec<OsString>,
+    },
+    /// 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 {
+        #[arg(long, env = "RUSTC_GIT")]
+        rustc_git: Option<String>,
+        github_user: String,
+        branch: String,
+    },
 }
+
 fn main() -> Result<()> {
-    let args = arg::Cli::parse();
-    commands::MiriRunner::exec(&args.commands)?;
+    let args = Cli::parse();
+    args.command.exec()?;
     Ok(())
 }
diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs
new file mode 100644
index 00000000000..caebd88b14b
--- /dev/null
+++ b/src/tools/miri/miri-script/src/util.rs
@@ -0,0 +1,153 @@
+use std::ffi::{OsStr, OsString};
+use std::path::PathBuf;
+
+use anyhow::{Context, Result};
+use dunce::canonicalize;
+use path_macro::path;
+use xshell::{cmd, Shell};
+
+pub fn miri_dir() -> std::io::Result<PathBuf> {
+    const MIRI_SCRIPT_ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR");
+    Ok(canonicalize(MIRI_SCRIPT_ROOT_DIR)?.parent().unwrap().into())
+}
+
+/// Queries the active toolchain for the Miri dir.
+pub fn active_toolchain() -> Result<String> {
+    let sh = shell()?;
+    sh.change_dir(miri_dir()?);
+    let stdout = cmd!(sh, "rustup show active-toolchain").read()?;
+    Ok(stdout.split_whitespace().next().context("Could not obtain active Rust toolchain")?.into())
+}
+
+pub fn shell() -> Result<Shell> {
+    let sh = Shell::new()?;
+    // xshell does not propagate parent's env variables by default.
+    for (k, v) in std::env::vars_os() {
+        sh.set_var(k, v);
+    }
+    Ok(sh)
+}
+
+pub fn flagsplit(flags: &str) -> Vec<String> {
+    // This code is taken from `RUSTFLAGS` handling in cargo.
+    flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
+}
+
+/// Some extra state we track for building Miri, such as the right RUSTFLAGS.
+pub struct MiriEnv {
+    /// miri_dir is the root of the miri repository checkout we are working in.
+    pub miri_dir: PathBuf,
+    /// active_toolchain is passed as `+toolchain` argument to cargo/rustc invocations.
+    pub toolchain: String,
+    /// Extra flags to pass to cargo.
+    pub cargo_extra_flags: Vec<String>,
+    /// The rustc sysroot
+    pub sysroot: PathBuf,
+    /// The shell we use.
+    pub sh: Shell,
+}
+
+impl MiriEnv {
+    pub fn new() -> Result<Self> {
+        let sh = shell()?;
+        let toolchain = active_toolchain()?;
+        let miri_dir = miri_dir()?;
+
+        let sysroot = cmd!(sh, "rustc +{toolchain} --print sysroot").read()?.into();
+        let target_output = cmd!(sh, "rustc +{toolchain} --version --verbose").read()?;
+        let rustc_meta = rustc_version::version_meta_for(&target_output)?;
+        let libdir = path!(sysroot / "lib" / "rustlib" / rustc_meta.host / "lib");
+
+        // Determine some toolchain properties
+        if !libdir.exists() {
+            println!("Something went wrong determining the library dir.");
+            println!("I got {} but that does not exist.", libdir.display());
+            println!("Please report a bug at https://github.com/rust-lang/miri/issues.");
+            std::process::exit(2);
+        }
+        // Share target dir between `miri` and `cargo-miri`.
+        let target_dir = std::env::var_os("CARGO_TARGET_DIR")
+            .unwrap_or_else(|| path!(miri_dir / "target").into());
+        sh.set_var("CARGO_TARGET_DIR", target_dir);
+
+        // We configure dev builds to not be unusably slow.
+        let devel_opt_level =
+            std::env::var_os("CARGO_PROFILE_DEV_OPT_LEVEL").unwrap_or_else(|| "2".into());
+        sh.set_var("CARGO_PROFILE_DEV_OPT_LEVEL", devel_opt_level);
+
+        // Compute rustflags.
+        let rustflags = {
+            let env = std::env::var_os("RUSTFLAGS");
+            let mut flags_with_warnings = OsString::from(
+                "-Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros ",
+            );
+            if let Some(value) = env {
+                flags_with_warnings.push(value);
+            }
+            // We set the rpath so that Miri finds the private rustc libraries it needs.
+            let mut flags_with_compiler_settings = OsString::from("-C link-args=-Wl,-rpath,");
+            flags_with_compiler_settings.push(&libdir);
+            flags_with_compiler_settings.push(flags_with_warnings);
+            flags_with_compiler_settings
+        };
+        sh.set_var("RUSTFLAGS", rustflags);
+
+        // Get extra flags for cargo.
+        let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
+        let cargo_extra_flags = flagsplit(&cargo_extra_flags);
+
+        Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags })
+    }
+
+    pub fn install_to_sysroot(
+        &self,
+        path: impl AsRef<OsStr>,
+        args: impl IntoIterator<Item = impl AsRef<OsStr>>,
+    ) -> Result<()> {
+        let MiriEnv { sysroot, toolchain, cargo_extra_flags, .. } = self;
+        // Install binaries to the miri toolchain's `sysroot` so they do not interact with other toolchains.
+        cmd!(self.sh, "cargo +{toolchain} install {cargo_extra_flags...} --path {path} --force --root {sysroot} {args...}").run()?;
+        Ok(())
+    }
+
+    pub fn build(
+        &self,
+        manifest_path: impl AsRef<OsStr>,
+        args: &[OsString],
+        quiet: bool,
+    ) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        let quiet_flag = if quiet { Some("--quiet") } else { None };
+        let mut cmd = cmd!(
+            self.sh,
+            "cargo +{toolchain} build {cargo_extra_flags...} --manifest-path {manifest_path} {quiet_flag...} {args...}"
+        );
+        cmd.set_quiet(quiet);
+        cmd.run()?;
+        Ok(())
+    }
+
+    pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
+            .run()?;
+        Ok(())
+    }
+
+    pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
+            .run()?;
+        Ok(())
+    }
+
+    pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+        let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
+        cmd!(
+            self.sh,
+            "cargo +{toolchain} test {cargo_extra_flags...} --manifest-path {manifest_path} {args...}"
+        )
+        .run()?;
+        Ok(())
+    }
+}