Skip to content

Commit 2091c16

Browse files
committed
some better error handling, check-only builds, fewer UTF-8 assumptions
1 parent e96b157 commit 2091c16

File tree

1 file changed

+63
-25
lines changed

1 file changed

+63
-25
lines changed

cargo-miri/bin.rs

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::env;
2-
use std::ffi::OsString;
2+
use std::ffi::{OsStr, OsString};
33
use std::fs::{self, File};
44
use std::io::{self, BufRead, BufReader, BufWriter, Write};
55
use std::ops::Not;
@@ -81,27 +81,29 @@ fn show_error(msg: String) -> ! {
8181

8282
// Determines whether a `--flag` is present.
8383
fn has_arg_flag(name: &str) -> bool {
84-
let mut args = std::env::args().take_while(|val| val != "--");
85-
args.any(|val| val == name)
84+
let mut args = std::env::args_os().take_while(|val| val.to_str() != Some("--"));
85+
args.any(|val| val.to_str() == Some(name))
8686
}
8787

8888
/// Gets the value of a `--flag`.
8989
fn get_arg_flag_value(name: &str) -> Option<String> {
9090
// Stop searching at `--`.
91-
let mut args = std::env::args().take_while(|val| val != "--");
91+
let mut args = std::env::args_os().take_while(|val| val.to_str() != Some("--"));
9292
loop {
9393
let arg = match args.next() {
9494
Some(arg) => arg,
9595
None => return None,
9696
};
97-
if !arg.starts_with(name) {
97+
if !arg.to_string_lossy().starts_with(name) {
9898
continue;
9999
}
100+
let arg = arg.to_str().expect("non-UTF-8 value not supported for flag we have to work with");
100101
// Strip leading `name`.
101102
let suffix = &arg[name.len()..];
102103
if suffix.is_empty() {
103104
// This argument is exactly `name`; the next one is the value.
104-
return args.next();
105+
let val = args.next().expect("value missing for flag");
106+
return Some(val.into_string().expect("non-UTF-8 value not supported for flag we have to work with"));
105107
} else if suffix.starts_with('=') {
106108
// This argument is `name=value`; get the value.
107109
// Strip leading `=`.
@@ -420,7 +422,7 @@ fn phase_cargo_miri(mut args: env::ArgsOs) {
420422
// Run cargo.
421423
if verbose {
422424
cmd.env("MIRI_VERBOSE", ""); // this makes `inside_cargo_rustc` verbose.
423-
eprintln!("+ {:?}", cmd);
425+
eprintln!("[cargo-miri miri] {:?}", cmd);
424426
}
425427
exec(cmd)
426428
}
@@ -470,19 +472,41 @@ fn phase_cargo_rustc(args: env::ArgsOs) {
470472
// (and cargo passes this before the filename so it should be unique)
471473
get_arg_flag_value("extra-filename").unwrap_or(String::new()),
472474
));
475+
if verbose {
476+
eprintln!("[cargo-miri rustc] writing run info to {:?}", path.display());
477+
}
473478

474479
let file = File::create(&path)
475-
.unwrap_or_else(|_| show_error(format!("Cannot create {}", path.display())));
480+
.unwrap_or_else(|_| show_error(format!("Cannot create {:?}", path.display())));
476481
let file = BufWriter::new(file);
477482
serde_json::ser::to_writer(file, &info)
478-
.unwrap_or_else(|_| show_error(format!("Cannot write to {}", path.display())));
483+
.unwrap_or_else(|_| show_error(format!("Cannot write to {:?}", path.display())));
479484
return;
480485
}
481486

482487
let mut cmd = miri();
483-
// Forward arguments.
484-
cmd.args(args);
485-
// FIXME: Make the build check-only!
488+
// Forward arguments, but (only for target crates!) remove "link" from "--emit" to make this a check-only build.
489+
let emit_flag = "--emit";
490+
for arg in args {
491+
if target_crate && arg.to_string_lossy().starts_with(emit_flag) {
492+
// Patch this argument. First, extract its value.
493+
let arg = arg.to_str().expect("`--emit` must be UTF-8");
494+
let val = &arg[emit_flag.len()..];
495+
assert!(val.starts_with("="), "`cargo` should pass `--emit=X` as one argument");
496+
let val = &val[1..];
497+
let mut val: Vec<_> = val.split(',').collect();
498+
// Now make sure "link" is not in there, but "metadata" is.
499+
if let Some(i) = val.iter().position(|&s| s == "link") {
500+
val.remove(i);
501+
if !val.iter().any(|&s| s == "metadata") {
502+
val.push("metadata");
503+
}
504+
}
505+
cmd.arg(format!("{}={}", emit_flag, val.join(",")));
506+
} else {
507+
cmd.arg(arg);
508+
}
509+
}
486510

487511
// We make sure to only specify our custom Xargo sysroot for target crates - that is,
488512
// crates which are needed for interpretation by Miri. proc-macros and build scripts
@@ -500,36 +524,50 @@ fn phase_cargo_rustc(args: env::ArgsOs) {
500524

501525
// Run it.
502526
if verbose {
503-
eprintln!("+ {:?}", cmd);
527+
eprintln!("[cargo-miri rustc] {:?}", cmd);
504528
}
505529
exec(cmd)
506530
}
507531

508-
fn phase_cargo_runner(binary: &str, args: env::ArgsOs) {
532+
fn phase_cargo_runner(binary: &OsStr, binary_args: env::ArgsOs) {
509533
let verbose = std::env::var_os("MIRI_VERBOSE").is_some();
510534

511535
let file = File::open(binary)
512-
.unwrap_or_else(|_| show_error(format!("File {:?} not found, or cargo-miri invoked incorrectly", binary)));
536+
.unwrap_or_else(|_| show_error(format!("File {:?} not found or `cargo-miri` invoked incorrectly; please only invoke this binary through `cargo miri`", binary)));
513537
let file = BufReader::new(file);
514538
let info: CrateRunInfo = serde_json::from_reader(file)
515539
.unwrap_or_else(|_| show_error(format!("File {:?} does not contain valid JSON", binary)));
516-
// FIXME: remove the file.
540+
fs::remove_file(binary)
541+
.unwrap_or_else(|_| show_error(format!("Unable to remove file {:?}", binary)));
517542

518543
let mut cmd = miri();
519-
// Forward rustc arguments,with our sysroot.
520-
cmd.args(info.args);
544+
// Forward rustc arguments, with our sysroot. We need to patch "--extern" filenames because
545+
// we forced a check-only build without cargo knowing about that: replace `.rlib` suffix by `.rmeta`.
546+
let mut args = info.args.into_iter();
547+
let extern_flag = "--extern";
548+
while let Some(arg) = args.next() {
549+
if arg.to_str() == Some(extern_flag) {
550+
let next_arg = args.next().expect("`--extern` shoulw be followed by a filename");
551+
let next_arg = next_arg.to_str().expect("Cannot work with non-UTF-8 extern filenames");
552+
let next_arg = next_arg.strip_suffix(".rlib").expect("all extern filenames should end in `.rlib`");
553+
cmd.arg(extern_flag);
554+
cmd.arg(format!("{}.rmeta", next_arg));
555+
} else {
556+
cmd.arg(arg);
557+
}
558+
}
521559
let sysroot =
522560
env::var_os("MIRI_SYSROOT").expect("The wrapper should have set MIRI_SYSROOT");
523561
cmd.arg("--sysroot");
524562
cmd.arg(sysroot);
525563

526564
// Then pass binary arguments.
527565
cmd.arg("--");
528-
cmd.args(args);
566+
cmd.args(binary_args);
529567

530568
// Run it.
531569
if verbose {
532-
eprintln!("+ {:?}", cmd);
570+
eprintln!("[cargo-miri runner] {:?}", cmd);
533571
}
534572
exec(cmd)
535573
}
@@ -556,10 +594,10 @@ fn main() {
556594
// binary crates for later interpretation.
557595
// - When we are executed due to CARGO_TARGET_RUNNER, we start interpretation based on the
558596
// flags that were stored earlier.
559-
// FIXME: report errors for these unwraps.
560-
match &*args.next().unwrap().to_str().unwrap() {
561-
"miri" => phase_cargo_miri(args),
562-
"rustc" => phase_cargo_rustc(args),
563-
binary => phase_cargo_runner(binary, args),
597+
match args.next().as_deref() {
598+
Some(s) if s.to_str() == Some("miri") => phase_cargo_miri(args),
599+
Some(s) if s.to_str() == Some("rustc") => phase_cargo_rustc(args),
600+
Some(binary) => phase_cargo_runner(binary, args),
601+
_ => show_error(format!("`cargo-miri` called without first argument; please only invoke this binary through `cargo miri`")),
564602
}
565603
}

0 commit comments

Comments
 (0)