Skip to content

Commit 1ee73b6

Browse files
committed
Auto merge of #3881 - RalfJung:miri-run, r=RalfJung
./miri run: directly run binary instead of using 'cargo run' This avoids re-building miri without dev dependencies, so it makes `./miri run` a lot faster if `./miri test` was already executed before.
2 parents c0fb1a7 + 1e651d7 commit 1ee73b6

File tree

5 files changed

+135
-51
lines changed

5 files changed

+135
-51
lines changed

ci/ci.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ time ./miri install
2626
# We enable all features to make sure the Stacked Borrows consistency check runs.
2727
echo "Building debug version of Miri"
2828
export CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS --all-features"
29-
time ./miri build --all-targets # the build that all the `./miri test` below will use
29+
time ./miri build # the build that all the `./miri test` below will use
3030

3131
endgroup
3232

miri-script/Cargo.lock

+88-30
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

miri-script/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ xshell = "0.2.6"
2323
rustc_version = "0.4"
2424
dunce = "1.0.4"
2525
directories = "5"
26+
serde_json = "1"

miri-script/src/commands.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -494,23 +494,21 @@ impl Command {
494494
flags: Vec<String>,
495495
) -> Result<()> {
496496
let mut e = MiriEnv::new()?;
497+
498+
// Preparation: get a sysroot, and get the miri binary.
499+
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
500+
let miri_bin =
501+
e.build_get_binary(".").context("failed to get filename of miri executable")?;
502+
497503
// More flags that we will pass before `flags`
498504
// (because `flags` may contain `--`).
499505
let mut early_flags = Vec::<OsString>::new();
500-
501-
// Add target, edition to flags.
502506
if let Some(target) = &target {
503507
early_flags.push("--target".into());
504508
early_flags.push(target.into());
505509
}
506-
if verbose {
507-
early_flags.push("--verbose".into());
508-
}
509510
early_flags.push("--edition".into());
510511
early_flags.push(edition.as_deref().unwrap_or("2021").into());
511-
512-
// Prepare a sysroot, add it to the flags. (Also builds cargo-miri, which we need.)
513-
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
514512
early_flags.push("--sysroot".into());
515513
early_flags.push(miri_sysroot.into());
516514

@@ -529,7 +527,8 @@ impl Command {
529527
.arg("--")
530528
.args(&["--miri-run-dep-mode"])
531529
} else {
532-
e.cargo_cmd(".", "run").args(quiet_flag).arg("--")
530+
// We have to run this with `rustup run` to avoid failures on Windows.
531+
e.toolchain_cmd(&miri_bin)
533532
};
534533
cmd.set_quiet(!verbose);
535534
// Add Miri flags

miri-script/src/util.rs

+37-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::ffi::{OsStr, OsString};
2+
use std::io::BufRead;
23
use std::ops::Range;
34
use std::path::{Path, PathBuf};
45
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
56
use std::thread;
67

7-
use anyhow::{anyhow, Context, Result};
8+
use anyhow::{anyhow, bail, Context, Result};
89
use dunce::canonicalize;
910
use path_macro::path;
1011
use xshell::{cmd, Cmd, Shell};
@@ -111,6 +112,12 @@ impl MiriEnv {
111112
)
112113
}
113114

115+
/// Prepares a command to be run in the toolchain (via `rustup run`).
116+
pub fn toolchain_cmd(&self, cmd: impl AsRef<OsStr>) -> Cmd<'_> {
117+
let MiriEnv { toolchain, .. } = self;
118+
cmd!(self.sh, "rustup run {toolchain} {cmd}")
119+
}
120+
114121
pub fn install_to_sysroot(
115122
&self,
116123
path: impl AsRef<OsStr>,
@@ -126,21 +133,40 @@ impl MiriEnv {
126133

127134
pub fn build(&self, crate_dir: impl AsRef<OsStr>, args: &[String], quiet: bool) -> Result<()> {
128135
let quiet_flag = if quiet { Some("--quiet") } else { None };
129-
// We build the tests as well, (a) to avoid having rebuilds when building the tests later
130-
// and (b) to have more parallelism during the build of Miri and its tests.
131-
// This means `./miri run` without `--dep` will build Miri twice (for the sysroot with
132-
// dev-dependencies, and then for running without dev-dependencies), but the way more common
133-
// `./miri test` will avoid building Miri twice.
134-
let mut cmd = self
135-
.cargo_cmd(crate_dir, "build")
136-
.args(&["--bins", "--tests"])
137-
.args(quiet_flag)
138-
.args(args);
136+
// We build all targets, since building *just* the bin target doesnot include
137+
// `dev-dependencies` and that changes feature resolution. This also gets us more
138+
// parallelism in `./miri test` as we build Miri and its tests together.
139+
let mut cmd =
140+
self.cargo_cmd(crate_dir, "build").args(&["--all-targets"]).args(quiet_flag).args(args);
139141
cmd.set_quiet(quiet);
140142
cmd.run()?;
141143
Ok(())
142144
}
143145

146+
/// Returns the path to the main crate binary. Assumes that `build` has been called before.
147+
pub fn build_get_binary(&self, crate_dir: impl AsRef<OsStr>) -> Result<PathBuf> {
148+
let cmd =
149+
self.cargo_cmd(crate_dir, "build").args(&["--all-targets", "--message-format=json"]);
150+
let output = cmd.output()?;
151+
let mut bin = None;
152+
for line in output.stdout.lines() {
153+
let line = line?;
154+
if line.starts_with("{") {
155+
let json: serde_json::Value = serde_json::from_str(&line)?;
156+
if json["reason"] == "compiler-artifact"
157+
&& !json["profile"]["test"].as_bool().unwrap()
158+
&& !json["executable"].is_null()
159+
{
160+
if bin.is_some() {
161+
bail!("found two binaries in cargo output");
162+
}
163+
bin = Some(PathBuf::from(json["executable"].as_str().unwrap()))
164+
}
165+
}
166+
}
167+
bin.ok_or_else(|| anyhow!("found no binary in cargo output"))
168+
}
169+
144170
pub fn check(&self, crate_dir: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
145171
self.cargo_cmd(crate_dir, "check").arg("--all-targets").args(args).run()?;
146172
Ok(())

0 commit comments

Comments
 (0)