Skip to content

Commit f7c938a

Browse files
committed
miri-script: use --remap-path-prefix to print errors relative to the right root
1 parent d36e157 commit f7c938a

File tree

9 files changed

+92
-75
lines changed

9 files changed

+92
-75
lines changed

src/tools/miri/.cargo/config.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[unstable]
2+
profile-rustflags = true
3+
4+
# Add back the containing directory of the packages we have to refer to using --manifest-path.
5+
# Per-package profiles avoid adding this to build dependencies.
6+
[profile.dev.package."cargo-miri"]
7+
rustflags = ["--remap-path-prefix", "=cargo-miri"]
8+
[profile.dev.package."miri-script"]
9+
rustflags = ["--remap-path-prefix", "=miri-script"]

src/tools/miri/CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ anyone but Miri itself. They are used to communicate between different Miri
309309
binaries, and as such worth documenting:
310310

311311
* `CARGO_EXTRA_FLAGS` is understood by `./miri` and passed to all host cargo invocations.
312+
It is reserved for CI usage; setting the wrong flags this way can easily confuse the script.
312313
* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to
313314
actually not interpret the code but compile it like rustc would. With `target`, Miri sets
314315
some compiler flags to prepare the code for interpretation; with `host`, this is not done.

src/tools/miri/cargo-miri/miri

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/tools/miri/cargo-miri/src/util.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,9 @@ pub fn find_miri() -> PathBuf {
9393
if let Some(path) = env::var_os("MIRI") {
9494
return path.into();
9595
}
96+
// Assume it is in the same directory as ourselves.
9697
let mut path = std::env::current_exe().expect("current executable path invalid");
97-
if cfg!(windows) {
98-
path.set_file_name("miri.exe");
99-
} else {
100-
path.set_file_name("miri");
101-
}
98+
path.set_file_name(format!("miri{}", env::consts::EXE_SUFFIX));
10299
path
103100
}
104101

src/tools/miri/miri

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
#!/usr/bin/env bash
22
set -e
3+
# We want to call the binary directly, so we need to know where it ends up.
4+
MIRI_SCRIPT_TARGET_DIR="$(dirname "$0")"/miri-script/target
5+
# If stdout is not a terminal and we are not on CI, assume that we are being invoked by RA, and use JSON output.
6+
if ! [ -t 1 ] && [ -z "$CI" ]; then
7+
MESSAGE_FORMAT="--message-format=json"
8+
fi
9+
# We need a nightly toolchain, for the `profile-rustflags` cargo feature.
10+
cargo +nightly build $CARGO_EXTRA_FLAGS --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml \
11+
-q --target-dir "$MIRI_SCRIPT_TARGET_DIR" $MESSAGE_FORMAT || \
12+
( echo "Failed to build miri-script. Is the 'nightly' toolchain installed?"; exit 1 )
313
# Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
414
# rustup (that sets it's own environmental variables), which is undesirable.
5-
MIRI_SCRIPT_TARGET_DIR="$(dirname "$0")"/miri-script/target
6-
cargo +stable build $CARGO_EXTRA_FLAGS -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml || \
7-
( echo "Failed to build miri-script. Is the 'stable' toolchain installed?"; exit 1 )
815
"$MIRI_SCRIPT_TARGET_DIR"/debug/miri-script "$@"

src/tools/miri/miri-script/miri

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/tools/miri/miri-script/src/commands.rs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const JOSH_FILTER: &str =
2222
const JOSH_PORT: &str = "42042";
2323

2424
impl MiriEnv {
25+
/// Prepares the environment: builds miri and cargo-miri and a sysroot.
2526
/// Returns the location of the sysroot.
2627
///
2728
/// If the target is None the sysroot will be built for the host machine.
@@ -35,7 +36,6 @@ impl MiriEnv {
3536
return Ok(miri_sysroot.into());
3637
}
3738
let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml");
38-
let Self { toolchain, cargo_extra_flags, .. } = &self;
3939

4040
// Make sure everything is built. Also Miri itself.
4141
self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
@@ -56,10 +56,12 @@ impl MiriEnv {
5656
eprintln!();
5757
}
5858

59-
let mut cmd = cmd!(self.sh,
60-
"cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} --
61-
miri setup --print-sysroot {target_flag...}"
62-
);
59+
let mut cmd = self
60+
.cargo_cmd(&manifest_path, "run")
61+
.arg("--quiet")
62+
.arg("--")
63+
.args(&["miri", "setup", "--print-sysroot"])
64+
.args(target_flag);
6365
cmd.set_quiet(quiet);
6466
let output = cmd.read()?;
6567
self.sh.set_var("MIRI_SYSROOT", &output);
@@ -459,7 +461,7 @@ impl Command {
459461
fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
460462
let mut e = MiriEnv::new()?;
461463

462-
// Prepare a sysroot.
464+
// Prepare a sysroot. (Also builds cargo-miri, which we need.)
463465
e.build_miri_sysroot(/* quiet */ false, target.as_deref())?;
464466

465467
// Forward information to test harness.
@@ -504,7 +506,7 @@ impl Command {
504506
early_flags.push("--edition".into());
505507
early_flags.push(edition.as_deref().unwrap_or("2021").into());
506508

507-
// Prepare a sysroot, add it to the flags.
509+
// Prepare a sysroot, add it to the flags. (Also builds cargo-miri, which we need.)
508510
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
509511
early_flags.push("--sysroot".into());
510512
early_flags.push(miri_sysroot.into());
@@ -513,23 +515,19 @@ impl Command {
513515
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
514516
let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default();
515517
let miri_flags = flagsplit(&miri_flags);
516-
let toolchain = &e.toolchain;
517-
let extra_flags = &e.cargo_extra_flags;
518518
let quiet_flag = if verbose { None } else { Some("--quiet") };
519519
// This closure runs the command with the given `seed_flag` added between the MIRIFLAGS and
520520
// the `flags` given on the command-line.
521-
let run_miri = |sh: &Shell, seed_flag: Option<String>| -> Result<()> {
521+
let run_miri = |e: &MiriEnv, seed_flag: Option<String>| -> Result<()> {
522522
// The basic command that executes the Miri driver.
523523
let mut cmd = if dep {
524-
cmd!(
525-
sh,
526-
"cargo +{toolchain} {quiet_flag...} test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode"
527-
)
524+
e.cargo_cmd(&miri_manifest, "test")
525+
.args(&["--test", "ui"])
526+
.args(quiet_flag)
527+
.arg("--")
528+
.args(&["--miri-run-dep-mode"])
528529
} else {
529-
cmd!(
530-
sh,
531-
"cargo +{toolchain} {quiet_flag...} run {extra_flags...} --manifest-path {miri_manifest} --"
532-
)
530+
e.cargo_cmd(&miri_manifest, "run").args(quiet_flag).arg("--")
533531
};
534532
cmd.set_quiet(!verbose);
535533
// Add Miri flags
@@ -545,14 +543,14 @@ impl Command {
545543
};
546544
// Run the closure once or many times.
547545
if let Some(seed_range) = many_seeds {
548-
e.run_many_times(seed_range, |sh, seed| {
546+
e.run_many_times(seed_range, |e, seed| {
549547
eprintln!("Trying seed: {seed}");
550-
run_miri(sh, Some(format!("-Zmiri-seed={seed}"))).inspect_err(|_| {
548+
run_miri(e, Some(format!("-Zmiri-seed={seed}"))).inspect_err(|_| {
551549
eprintln!("FAILING SEED: {seed}");
552550
})
553551
})?;
554552
} else {
555-
run_miri(&e.sh, None)?;
553+
run_miri(&e, None)?;
556554
}
557555
Ok(())
558556
}
@@ -579,6 +577,6 @@ impl Command {
579577
.filter_ok(|item| item.file_type().is_file())
580578
.map_ok(|item| item.into_path());
581579

582-
e.format_files(files, &e.toolchain[..], &config_path, &flags)
580+
e.format_files(files, &config_path, &flags)
583581
}
584582
}

src/tools/miri/miri-script/src/util.rs

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::thread;
77
use anyhow::{anyhow, Context, Result};
88
use dunce::canonicalize;
99
use path_macro::path;
10-
use xshell::{cmd, Shell};
10+
use xshell::{cmd, Cmd, Shell};
1111

1212
pub fn miri_dir() -> std::io::Result<PathBuf> {
1313
const MIRI_SCRIPT_ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR");
@@ -28,13 +28,14 @@ pub fn flagsplit(flags: &str) -> Vec<String> {
2828
}
2929

3030
/// Some extra state we track for building Miri, such as the right RUSTFLAGS.
31+
#[derive(Clone)]
3132
pub struct MiriEnv {
3233
/// miri_dir is the root of the miri repository checkout we are working in.
3334
pub miri_dir: PathBuf,
3435
/// active_toolchain is passed as `+toolchain` argument to cargo/rustc invocations.
3536
pub toolchain: String,
3637
/// Extra flags to pass to cargo.
37-
pub cargo_extra_flags: Vec<String>,
38+
cargo_extra_flags: Vec<String>,
3839
/// The rustc sysroot
3940
pub sysroot: PathBuf,
4041
/// The shell we use.
@@ -54,15 +55,14 @@ impl MiriEnv {
5455

5556
// Determine some toolchain properties
5657
if !libdir.exists() {
57-
println!("Something went wrong determining the library dir.");
58-
println!("I got {} but that does not exist.", libdir.display());
59-
println!("Please report a bug at https://github.com/rust-lang/miri/issues.");
58+
eprintln!("Something went wrong determining the library dir.");
59+
eprintln!("I got {} but that does not exist.", libdir.display());
60+
eprintln!("Please report a bug at https://github.com/rust-lang/miri/issues.");
6061
std::process::exit(2);
6162
}
62-
// Share target dir between `miri` and `cargo-miri`.
63-
let target_dir = std::env::var_os("CARGO_TARGET_DIR")
64-
.unwrap_or_else(|| path!(miri_dir / "target").into());
65-
sh.set_var("CARGO_TARGET_DIR", target_dir);
63+
64+
// Hard-code the target dir, since we rely on all binaries ending up in the same spot.
65+
sh.set_var("CARGO_TARGET_DIR", path!(miri_dir / "target"));
6666

6767
// We configure dev builds to not be unusably slow.
6868
let devel_opt_level =
@@ -91,17 +91,34 @@ impl MiriEnv {
9191
// Get extra flags for cargo.
9292
let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
9393
let cargo_extra_flags = flagsplit(&cargo_extra_flags);
94+
if cargo_extra_flags.iter().any(|a| a == "--release" || a.starts_with("--profile")) {
95+
// This makes binaries end up in different paths, let's not do that.
96+
eprintln!(
97+
"Passing `--release` or `--profile` in `CARGO_EXTRA_FLAGS` will totally confuse miri-script, please don't do that."
98+
);
99+
std::process::exit(1);
100+
}
94101

95102
Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags })
96103
}
97104

105+
pub fn cargo_cmd(&self, manifest_path: impl AsRef<OsStr>, cmd: &str) -> Cmd<'_> {
106+
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
107+
let manifest_path = Path::new(manifest_path.as_ref());
108+
cmd!(
109+
self.sh,
110+
"cargo +{toolchain} {cmd} {cargo_extra_flags...} --manifest-path {manifest_path}"
111+
)
112+
}
113+
98114
pub fn install_to_sysroot(
99115
&self,
100116
path: impl AsRef<OsStr>,
101117
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
102118
) -> Result<()> {
103119
let MiriEnv { sysroot, toolchain, cargo_extra_flags, .. } = self;
104120
// Install binaries to the miri toolchain's `sysroot` so they do not interact with other toolchains.
121+
// (Not using `cargo_cmd` as `install` is special and doesn't use `--manifest-path`.)
105122
cmd!(self.sh, "cargo +{toolchain} install {cargo_extra_flags...} --path {path} --force --root {sysroot} {args...}").run()?;
106123
Ok(())
107124
}
@@ -112,40 +129,34 @@ impl MiriEnv {
112129
args: &[String],
113130
quiet: bool,
114131
) -> Result<()> {
115-
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
116132
let quiet_flag = if quiet { Some("--quiet") } else { None };
117133
// We build the tests as well, (a) to avoid having rebuilds when building the tests later
118134
// and (b) to have more parallelism during the build of Miri and its tests.
119-
let mut cmd = cmd!(
120-
self.sh,
121-
"cargo +{toolchain} build --bins --tests {cargo_extra_flags...} --manifest-path {manifest_path} {quiet_flag...} {args...}"
122-
);
135+
// This means `./miri run` without `--dep` will build Miri twice (for the sysroot with
136+
// dev-dependencies, and then for running without dev-dependencies), but the way more common
137+
// `./miri test` will avoid building Miri twice.
138+
let mut cmd = self
139+
.cargo_cmd(manifest_path, "build")
140+
.args(&["--bins", "--tests"])
141+
.args(quiet_flag)
142+
.args(args);
123143
cmd.set_quiet(quiet);
124144
cmd.run()?;
125145
Ok(())
126146
}
127147

128148
pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
129-
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
130-
cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
131-
.run()?;
149+
self.cargo_cmd(manifest_path, "check").arg("--all-targets").args(args).run()?;
132150
Ok(())
133151
}
134152

135153
pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
136-
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
137-
cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
138-
.run()?;
154+
self.cargo_cmd(manifest_path, "clippy").arg("--all-targets").args(args).run()?;
139155
Ok(())
140156
}
141157

142158
pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
143-
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
144-
cmd!(
145-
self.sh,
146-
"cargo +{toolchain} test {cargo_extra_flags...} --manifest-path {manifest_path} {args...}"
147-
)
148-
.run()?;
159+
self.cargo_cmd(manifest_path, "test").args(args).run()?;
149160
Ok(())
150161
}
151162

@@ -155,7 +166,6 @@ impl MiriEnv {
155166
pub fn format_files(
156167
&self,
157168
files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>,
158-
toolchain: &str,
159169
config_path: &Path,
160170
flags: &[String],
161171
) -> anyhow::Result<()> {
@@ -166,6 +176,7 @@ impl MiriEnv {
166176
// Format in batches as not all our files fit into Windows' command argument limit.
167177
for batch in &files.chunks(256) {
168178
// Build base command.
179+
let toolchain = &self.toolchain;
169180
let mut cmd = cmd!(
170181
self.sh,
171182
"rustfmt +{toolchain} --edition=2021 --config-path {config_path} --unstable-features --skip-children {flags...}"
@@ -197,7 +208,7 @@ impl MiriEnv {
197208
pub fn run_many_times(
198209
&self,
199210
range: Range<u32>,
200-
run: impl Fn(&Shell, u32) -> Result<()> + Sync,
211+
run: impl Fn(&Self, u32) -> Result<()> + Sync,
201212
) -> Result<()> {
202213
// `next` is atomic so threads can concurrently fetch their next value to run.
203214
let next = AtomicU32::new(range.start);
@@ -207,10 +218,10 @@ impl MiriEnv {
207218
let mut handles = Vec::new();
208219
// Spawn one worker per core.
209220
for _ in 0..thread::available_parallelism()?.get() {
210-
// Create a copy of the shell for this thread.
211-
let local_shell = self.sh.clone();
221+
// Create a copy of the environment for this thread.
222+
let local_miri = self.clone();
212223
let handle = s.spawn(|| -> Result<()> {
213-
let local_shell = local_shell; // move the copy into this thread.
224+
let local_miri = local_miri; // move the copy into this thread.
214225
// Each worker thread keeps asking for numbers until we're all done.
215226
loop {
216227
let cur = next.fetch_add(1, Ordering::Relaxed);
@@ -219,7 +230,7 @@ impl MiriEnv {
219230
break;
220231
}
221232
// Run the command with this seed.
222-
run(&local_shell, cur).inspect_err(|_| {
233+
run(&local_miri, cur).inspect_err(|_| {
223234
// If we failed, tell everyone about this.
224235
failed.store(true, Ordering::Relaxed);
225236
})?;

src/tools/miri/tests/ui.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use ui_test::{
1313
};
1414

1515
fn miri_path() -> PathBuf {
16-
PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri")))
16+
PathBuf::from(env::var("MIRI").unwrap_or_else(|_| env!("CARGO_BIN_EXE_miri").into()))
1717
}
1818

1919
fn get_host() -> String {
@@ -29,7 +29,7 @@ pub fn flagsplit(flags: &str) -> Vec<String> {
2929

3030
// Build the shared object file for testing native function calls.
3131
fn build_native_lib() -> PathBuf {
32-
let cc = option_env!("CC").unwrap_or("cc");
32+
let cc = env::var("CC").unwrap_or_else(|_| "cc".into());
3333
// Target directory that we can write to.
3434
let so_target_dir =
3535
Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib");
@@ -84,9 +84,11 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
8484
if with_dependencies {
8585
// Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary.
8686
// (It's a separate crate, so we don't get an env var from cargo.)
87-
let mut prog = miri_path();
88-
prog.set_file_name("cargo-miri");
89-
config.dependency_builder.program = prog;
87+
config.dependency_builder.program = {
88+
let mut prog = miri_path();
89+
prog.set_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX));
90+
prog
91+
};
9092
let builder_args = ["miri", "run"]; // There is no `cargo miri build` so we just use `cargo miri run`.
9193
config.dependency_builder.args = builder_args.into_iter().map(Into::into).collect();
9294
config.dependencies_crate_manifest_path =

0 commit comments

Comments
 (0)