Skip to content

Commit 853254f

Browse files
committed
Auto merge of #1675 - hyd-dev:proc-macro-unit-test, r=RalfJung
[cargo-miri] Skip unit tests of `proc-macro` crates Fixes #1660.
2 parents c26fa83 + 2857792 commit 853254f

File tree

5 files changed

+106
-40
lines changed

5 files changed

+106
-40
lines changed

cargo-miri/bin.rs

Lines changed: 98 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::env;
22
use std::ffi::OsString;
33
use std::fs::{self, File};
44
use std::io::{self, BufRead, BufReader, BufWriter, Write};
5+
use std::iter::TakeWhile;
56
use std::ops::Not;
67
use std::path::{Path, PathBuf};
78
use std::process::Command;
@@ -36,9 +37,9 @@ enum MiriCommand {
3637
Setup,
3738
}
3839

39-
/// The inforamtion Miri needs to run a crate. Stored as JSON when the crate is "compiled".
40+
/// The information to run a crate with the given environment.
4041
#[derive(Serialize, Deserialize)]
41-
struct CrateRunInfo {
42+
struct CrateRunEnv {
4243
/// The command-line arguments.
4344
args: Vec<String>,
4445
/// The environment.
@@ -47,13 +48,22 @@ struct CrateRunInfo {
4748
current_dir: OsString,
4849
}
4950

51+
/// The information Miri needs to run a crate. Stored as JSON when the crate is "compiled".
52+
#[derive(Serialize, Deserialize)]
53+
enum CrateRunInfo {
54+
/// Run it with the given environment.
55+
RunWith(CrateRunEnv),
56+
/// Skip it as Miri does not support interpreting such kind of crates.
57+
SkipProcMacroTest,
58+
}
59+
5060
impl CrateRunInfo {
5161
/// Gather all the information we need.
5262
fn collect(args: env::Args) -> Self {
5363
let args = args.collect();
5464
let env = env::vars_os().collect();
5565
let current_dir = env::current_dir().unwrap().into_os_string();
56-
CrateRunInfo { args, env, current_dir }
66+
Self::RunWith(CrateRunEnv { args, env, current_dir })
5767
}
5868

5969
fn store(&self, filename: &Path) {
@@ -89,31 +99,50 @@ fn has_arg_flag(name: &str) -> bool {
8999
args.any(|val| val == name)
90100
}
91101

92-
/// Gets the value of a `--flag`.
93-
fn get_arg_flag_value(name: &str) -> Option<String> {
94-
// Stop searching at `--`.
95-
let mut args = std::env::args().take_while(|val| val != "--");
96-
loop {
97-
let arg = match args.next() {
98-
Some(arg) => arg,
99-
None => return None,
100-
};
101-
if !arg.starts_with(name) {
102-
continue;
102+
/// Yields all values of command line flag `name`.
103+
struct ArgFlagValueIter<'a> {
104+
args: TakeWhile<env::Args, fn(&String) -> bool>,
105+
name: &'a str,
106+
}
107+
108+
impl<'a> ArgFlagValueIter<'a> {
109+
fn new(name: &'a str) -> Self {
110+
Self {
111+
// Stop searching at `--`.
112+
args: env::args().take_while(|val| val != "--"),
113+
name,
103114
}
104-
// Strip leading `name`.
105-
let suffix = &arg[name.len()..];
106-
if suffix.is_empty() {
107-
// This argument is exactly `name`; the next one is the value.
108-
return args.next();
109-
} else if suffix.starts_with('=') {
110-
// This argument is `name=value`; get the value.
111-
// Strip leading `=`.
112-
return Some(suffix[1..].to_owned());
115+
}
116+
}
117+
118+
impl Iterator for ArgFlagValueIter<'_> {
119+
type Item = String;
120+
121+
fn next(&mut self) -> Option<Self::Item> {
122+
loop {
123+
let arg = self.args.next()?;
124+
if !arg.starts_with(self.name) {
125+
continue;
126+
}
127+
// Strip leading `name`.
128+
let suffix = &arg[self.name.len()..];
129+
if suffix.is_empty() {
130+
// This argument is exactly `name`; the next one is the value.
131+
return self.args.next();
132+
} else if suffix.starts_with('=') {
133+
// This argument is `name=value`; get the value.
134+
// Strip leading `=`.
135+
return Some(suffix[1..].to_owned());
136+
}
113137
}
114138
}
115139
}
116140

141+
/// Gets the value of a `--flag`.
142+
fn get_arg_flag_value(name: &str) -> Option<String> {
143+
ArgFlagValueIter::new(name).next()
144+
}
145+
117146
/// Returns the path to the `miri` binary
118147
fn find_miri() -> PathBuf {
119148
if let Some(path) = env::var_os("MIRI") {
@@ -436,14 +465,15 @@ fn phase_cargo_miri(mut args: env::Args) {
436465
// This is needed to make the `CARGO_TARGET_*_RUNNER` env var do something,
437466
// and it later helps us detect which crates are proc-macro/build-script
438467
// (host crates) and which crates are needed for the program itself.
439-
let target = if let Some(target) = get_arg_flag_value("--target") {
468+
let host = version_info().host;
469+
let target = get_arg_flag_value("--target");
470+
let target = if let Some(ref target) = target {
440471
target
441472
} else {
442473
// No target given. Pick default and tell cargo about it.
443-
let host = version_info().host;
444474
cmd.arg("--target");
445475
cmd.arg(&host);
446-
host
476+
&host
447477
};
448478

449479
// Forward all further arguments. We do some processing here because we want to
@@ -495,17 +525,27 @@ fn phase_cargo_miri(mut args: env::Args) {
495525
}
496526
cmd.env("RUSTC_WRAPPER", &cargo_miri_path);
497527

498-
// Set the runner for the current target to us as well, so we can interpret the binaries.
499-
let runner_env_name = format!("CARGO_TARGET_{}_RUNNER", target.to_uppercase().replace('-', "_"));
500-
cmd.env(&runner_env_name, &cargo_miri_path);
528+
let runner_env_name = |triple: &str| {
529+
format!("CARGO_TARGET_{}_RUNNER", triple.to_uppercase().replace('-', "_"))
530+
};
531+
let host_runner_env_name = runner_env_name(&host);
532+
let target_runner_env_name = runner_env_name(target);
533+
// Set the target runner to us, so we can interpret the binaries.
534+
cmd.env(&target_runner_env_name, &cargo_miri_path);
535+
// Unit tests of `proc-macro` crates are run on the host, so we set the host runner to
536+
// us in order to skip them.
537+
cmd.env(&host_runner_env_name, &cargo_miri_path);
501538

502539
// Set rustdoc to us as well, so we can make it do nothing (see issue #584).
503540
cmd.env("RUSTDOC", &cargo_miri_path);
504541

505542
// Run cargo.
506543
if verbose {
507544
eprintln!("[cargo-miri miri] RUSTC_WRAPPER={:?}", cargo_miri_path);
508-
eprintln!("[cargo-miri miri] {}={:?}", runner_env_name, cargo_miri_path);
545+
eprintln!("[cargo-miri miri] {}={:?}", target_runner_env_name, cargo_miri_path);
546+
if *target != host {
547+
eprintln!("[cargo-miri miri] {}={:?}", host_runner_env_name, cargo_miri_path);
548+
}
509549
eprintln!("[cargo-miri miri] RUSTDOC={:?}", cargo_miri_path);
510550
eprintln!("[cargo-miri miri] {:?}", cmd);
511551
cmd.env("MIRI_VERBOSE", ""); // This makes the other phases verbose.
@@ -568,23 +608,34 @@ fn phase_cargo_rustc(args: env::Args) {
568608
_ => {},
569609
}
570610

571-
if !print && target_crate && is_runnable_crate() {
572-
// This is the binary or test crate that we want to interpret under Miri.
573-
// But we cannot run it here, as cargo invoked us as a compiler -- our stdin and stdout are not
574-
// like we want them.
575-
// Instead of compiling, we write JSON into the output file with all the relevant command-line flags
576-
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
577-
let info = CrateRunInfo::collect(args);
611+
let store_json = |info: CrateRunInfo| {
578612
let filename = out_filename("", "");
579613
if verbose {
580614
eprintln!("[cargo-miri rustc] writing run info to `{}`", filename.display());
581615
}
582-
583616
info.store(&filename);
584617
// For Windows, do the same thing again with `.exe` appended to the filename.
585618
// (Need to do this here as cargo moves that "binary" to a different place before running it.)
586619
info.store(&out_filename("", ".exe"));
620+
};
621+
622+
let runnable_crate = !print && is_runnable_crate();
587623

624+
if runnable_crate && target_crate {
625+
// This is the binary or test crate that we want to interpret under Miri.
626+
// But we cannot run it here, as cargo invoked us as a compiler -- our stdin and stdout are not
627+
// like we want them.
628+
// Instead of compiling, we write JSON into the output file with all the relevant command-line flags
629+
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
630+
store_json(CrateRunInfo::collect(args));
631+
return;
632+
}
633+
634+
if runnable_crate && ArgFlagValueIter::new("--extern").any(|krate| krate == "proc_macro") {
635+
// This is a "runnable" `proc-macro` crate (unit tests). We do not support
636+
// interpreting that under Miri now, so we write a JSON file to (display a
637+
// helpful message and) skip it in the runner phase.
638+
store_json(CrateRunInfo::SkipProcMacroTest);
588639
return;
589640
}
590641

@@ -652,8 +703,16 @@ fn phase_cargo_runner(binary: &Path, binary_args: env::Args) {
652703
let file = File::open(&binary)
653704
.unwrap_or_else(|_| show_error(format!("file {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary)));
654705
let file = BufReader::new(file);
655-
let info: CrateRunInfo = serde_json::from_reader(file)
706+
707+
let info = serde_json::from_reader(file)
656708
.unwrap_or_else(|_| show_error(format!("file {:?} contains outdated or invalid JSON; try `cargo clean`", binary)));
709+
let info = match info {
710+
CrateRunInfo::RunWith(info) => info,
711+
CrateRunInfo::SkipProcMacroTest => {
712+
eprintln!("Running unit tests of `proc-macro` crates is not currently supported by Miri.");
713+
return;
714+
}
715+
};
657716

658717
let mut cmd = miri();
659718

test-cargo-miri/run-test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test_cargo_miri_test():
102102
)
103103
test("`cargo miri test` (subcrate, no isolation)",
104104
cargo_miri("test") + ["-p", "subcrate"],
105-
"test.subcrate.stdout.ref", "test.stderr-empty.ref",
105+
"test.subcrate.stdout.ref", "test.stderr-proc-macro.ref",
106106
env={'MIRIFLAGS': "-Zmiri-disable-isolation"},
107107
)
108108

test-cargo-miri/subcrate/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ version = "0.1.0"
44
authors = ["Miri Team"]
55
edition = "2018"
66

7+
[lib]
8+
proc-macro = true
9+
doctest = false
10+
711
[[bin]]
812
name = "subcrate"
913
path = "main.rs"

test-cargo-miri/subcrate/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[cfg(test)]
2+
compile_error!("Miri should not touch me");
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Running unit tests of `proc-macro` crates is not currently supported by Miri.

0 commit comments

Comments
 (0)