+#![feature(let_else)]
#![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq)]
mod version;
cargo miri test -- test-suite-filter
"#;
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug)]
enum MiriCommand {
- Run,
- Test,
+ /// Our own special 'setup' command.
Setup,
+ /// A command to be forwarded to cargo.
+ Forward(String),
}
/// The information to run a crate with the given environment.
}
}
+/// Writes the given content to the given file *cross-process atomically*, in the sense that another
+/// process concurrently reading that file will see either the old content or the new content, but
+/// not some intermediate (e.g., empty) state.
+///
+/// We assume no other parts of this same process are trying to read or write that file.
+fn write_to_file(filename: &Path, content: &str) {
+ // Create a temporary file with the desired contents.
+ let mut temp_filename = filename.as_os_str().to_os_string();
+ temp_filename.push(&format!(".{}", std::process::id()));
+ let mut temp_file = File::create(&temp_filename).unwrap();
+ temp_file.write_all(content.as_bytes()).unwrap();
+ drop(temp_file);
+
+ // Move file to the desired location.
+ fs::rename(temp_filename, filename).unwrap();
+}
+
/// Performs the setup required to make `cargo miri` work: Getting a custom-built libstd. Then sets
/// `MIRI_SYSROOT`. Skipped if `MIRI_SYSROOT` is already set, in which case we expect the user has
/// done all this already.
-fn setup(subcommand: MiriCommand) {
+fn setup(subcommand: &MiriCommand) {
+ let only_setup = matches!(subcommand, MiriCommand::Setup);
if std::env::var_os("MIRI_SYSROOT").is_some() {
- if subcommand == MiriCommand::Setup {
+ if only_setup {
println!("WARNING: MIRI_SYSROOT already set, not doing anything.")
}
return;
// Subcommands other than `setup` will do a setup if necessary, but
// interactively confirm first.
- let ask_user = subcommand != MiriCommand::Setup;
+ let ask_user = !only_setup;
// First, we need xargo.
if xargo_version().map_or(true, |v| v < XARGO_MIN_VERSION) {
}
None => {
// Check for `rust-src` rustup component.
- let sysroot = miri()
- .args(&["--print", "sysroot"])
- .output()
- .expect("failed to determine sysroot")
- .stdout;
- let sysroot = std::str::from_utf8(&sysroot).unwrap();
+ let output =
+ miri().args(&["--print", "sysroot"]).output().expect("failed to determine sysroot");
+ if !output.status.success() {
+ show_error(format!(
+ "Failed to determine sysroot; Miri said:\n{}",
+ String::from_utf8_lossy(&output.stderr).trim_end()
+ ));
+ }
+ let sysroot = std::str::from_utf8(&output.stdout).unwrap();
let sysroot = Path::new(sysroot.trim_end_matches('\n'));
// Check for `$SYSROOT/lib/rustlib/src/rust/library`; test if that contains `std/Cargo.toml`.
let rustup_src =
if !dir.exists() {
fs::create_dir_all(&dir).unwrap();
}
- // The interesting bit: Xargo.toml
- File::create(dir.join("Xargo.toml"))
- .unwrap()
- .write_all(
- br#"
+ // The interesting bit: Xargo.toml (only needs content if we actually need std)
+ let xargo_toml = if std::env::var_os("MIRI_NO_STD").is_some() {
+ ""
+ } else {
+ r#"
[dependencies.std]
default_features = false
# We support unwinding, so enable that panic runtime.
features = ["panic_unwind", "backtrace"]
[dependencies.test]
-"#,
- )
- .unwrap();
+"#
+ };
+ write_to_file(&dir.join("Xargo.toml"), xargo_toml);
// The boring bits: a dummy project for xargo.
// FIXME: With xargo-check, can we avoid doing this?
- File::create(dir.join("Cargo.toml"))
- .unwrap()
- .write_all(
- br#"
+ write_to_file(
+ &dir.join("Cargo.toml"),
+ r#"
[package]
name = "miri-xargo"
description = "A dummy project for building libstd with xargo."
[lib]
path = "lib.rs"
"#,
- )
- .unwrap();
- File::create(dir.join("lib.rs")).unwrap();
+ );
+ write_to_file(&dir.join("lib.rs"), "#![no_std]");
// Determine architectures.
// We always need to set a target so rustc bootstrap can tell apart host from target crates.
let sysroot = if target == &host { dir.join("HOST") } else { PathBuf::from(dir) };
std::env::set_var("MIRI_SYSROOT", &sysroot); // pass the env var to the processes we spawn, which will turn it into "--sysroot" flags
// Figure out what to print.
- let print_sysroot = subcommand == MiriCommand::Setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
+ let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
if print_sysroot {
// Print just the sysroot and nothing else; this way we do not need any escaping.
println!("{}", sysroot.display());
- } else if subcommand == MiriCommand::Setup {
+ } else if only_setup {
println!("A libstd for Miri is now available in `{}`.", sysroot.display());
}
}
// Require a subcommand before any flags.
// We cannot know which of those flags take arguments and which do not,
// so we cannot detect subcommands later.
- let subcommand = match args.next().as_deref() {
- Some("test" | "t") => MiriCommand::Test,
- Some("run" | "r") => MiriCommand::Run,
- Some("setup") => MiriCommand::Setup,
+ let Some(subcommand) = args.next() else {
+ show_error(format!("`cargo miri` needs to be called with a subcommand (`run`, `test`)"));
+ };
+ let subcommand = match &*subcommand {
+ "setup" => MiriCommand::Setup,
+ "test" | "t" | "run" | "r" => MiriCommand::Forward(subcommand),
// Invalid command.
_ =>
show_error(format!(
let verbose = has_arg_flag("-v");
// We always setup.
- setup(subcommand);
+ setup(&subcommand);
// Invoke actual cargo for the job, but with different flags.
// We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
// harder.
let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
let cargo_cmd = match subcommand {
- MiriCommand::Test => "test",
- MiriCommand::Run => "run",
+ MiriCommand::Forward(s) => s,
MiriCommand::Setup => return, // `cargo miri setup` stops here.
};
let mut cmd = cargo();
// Drop this argument.
} else if let Some(suffix) = arg.strip_prefix(json_flag) {
assert!(suffix.starts_with('='));
- // This is how we pass through --color=always. We detect that Cargo is detecting rustc
- // to emit the diagnostic structure that Cargo would consume from rustc to emit colored
- // diagnostics, and ask rustc to emit them.
- // See https://github.com/rust-lang/miri/issues/2037
- // First skip over the leading `=`, then check for diagnostic-rendered-ansi in the
- // comma-separated list
- if suffix.strip_prefix('=').unwrap().split(',').any(|a| a == "diagnostic-rendered-ansi")
- {
- cmd.arg("--color=always");
- }
- // But aside from remembering that colored output was requested, drop this argument.
+ // Drop this argument.
} else {
cmd.arg(arg);
}