diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 52f96a28c9..f49bc6d23b 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -192,12 +192,14 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { "WARNING: Ignoring `RUSTC` environment variable; set `MIRI` if you want to control the binary used as the driver." ); } - // Build scripts (and also cargo: https://github.com/rust-lang/cargo/issues/10885) will invoke - // `rustc` even when `RUSTC_WRAPPER` is set. To make sure everything is coherent, we want that - // to be the Miri driver, but acting as rustc, on the target level. (Target, rather than host, - // is needed for cross-interpretation situations.) This is not a perfect emulation of real rustc - // (it might be unable to produce binaries since the sysroot is check-only), but it's as close - // as we can get, and it's good enough for autocfg. + // Ideally we would set RUSTC to some non-existent path, so we can be sure our wrapping is + // always applied. However, buggy build scripts (https://github.com/eyre-rs/eyre/issues/84) and + // also cargo (https://github.com/rust-lang/cargo/issues/10885) will invoke `rustc` even when + // `RUSTC_WRAPPER` is set, bypassing the wrapper. To make sure everything is coherent, we want + // that to be the Miri driver, but acting as rustc, on the target level. (Target, rather than + // host, is needed for cross-interpretation situations.) This is not a perfect emulation of real + // rustc (it might be unable to produce binaries since the sysroot is check-only), but it's as + // close as we can get, and it's good enough for autocfg. // // In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc // or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that @@ -251,6 +253,16 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { /// Cargo does not give us this information directly, so we need to check /// various command-line flags. fn is_runnable_crate() -> bool { + // Determine whether this is cargo invoking rustc to get some infos. Ideally we'd check "is + // there a filename passed to rustc", but that's very hard as we would have to know whether + // e.g. `--print foo` is a booolean flag `--print` followed by filename `foo` or equivalent + // to `--print=foo`. So instead we use this more fragile approach of detecting the presence + // of a "query" flag rather than the absence of a filename. + let info_query = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV"); + if info_query { + // Nothing to run. + return false; + } let is_bin = get_arg_flag_value("--crate-type").as_deref().unwrap_or("bin") == "bin"; let is_test = has_arg_flag("--test"); is_bin || is_test @@ -297,8 +309,6 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { let verbose = std::env::var("MIRI_VERBOSE") .map_or(0, |verbose| verbose.parse().expect("verbosity flag must be an integer")); let target_crate = is_target_crate(); - // Determine whether this is cargo invoking rustc to get some infos. - let info_query = get_arg_flag_value("--print").is_some() || has_arg_flag("-vV"); let store_json = |info: CrateRunInfo| { if get_arg_flag_value("--emit").unwrap_or_default().split(',').any(|e| e == "dep-info") { @@ -325,7 +335,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } }; - let runnable_crate = !info_query && is_runnable_crate(); + let runnable_crate = is_runnable_crate(); if runnable_crate && target_crate { assert!( @@ -399,7 +409,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { let mut emit_link_hack = false; // Arguments are treated very differently depending on whether this crate is // for interpretation by Miri, or for use by a build script / proc macro. - if !info_query && target_crate { + if target_crate { // Forward arguments, but remove "link" from "--emit" to make this a check-only build. let emit_flag = "--emit"; while let Some(arg) = args.next() { @@ -433,17 +443,14 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { cmd.arg("-C").arg("panic=abort"); } } else { - // For host crates (but not when we are just printing some info), - // we might still have to set the sysroot. - if !info_query { - // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly - // due to bootstrap complications. - if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") { - cmd.arg("--sysroot").arg(sysroot); - } + // This is a host crate. + // When we're running `cargo-miri` from `x.py` we need to pass the sysroot explicitly + // due to bootstrap complications. + if let Some(sysroot) = std::env::var_os("MIRI_HOST_SYSROOT") { + cmd.arg("--sysroot").arg(sysroot); } - // For host crates or when we are printing, just forward everything. + // Forward everything. cmd.args(args); } @@ -455,9 +462,7 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // Run it. if verbose > 0 { - eprintln!( - "[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} info_query={info_query}" - ); + eprintln!("[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate}"); } // Create a stub .rlib file if "link" was requested by cargo. @@ -546,15 +551,13 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner // but when we run here, cargo does not interpret the JSON any more. `--json` // then also needs to be dropped. let mut args = info.args.into_iter(); - let error_format_flag = "--error-format"; - let json_flag = "--json"; while let Some(arg) = args.next() { if arg == "--extern" { forward_patched_extern_arg(&mut args, &mut cmd); - } else if let Some(suffix) = arg.strip_prefix(error_format_flag) { + } else if let Some(suffix) = arg.strip_prefix("--error-format") { assert!(suffix.starts_with('=')); // Drop this argument. - } else if let Some(suffix) = arg.strip_prefix(json_flag) { + } else if let Some(suffix) = arg.strip_prefix("--json") { assert!(suffix.starts_with('=')); // Drop this argument. } else { @@ -592,13 +595,11 @@ pub fn phase_rustdoc(mut args: impl Iterator) { // just default to a straight-forward invocation for now: let mut cmd = Command::new("rustdoc"); - let extern_flag = "--extern"; - let runtool_flag = "--runtool"; while let Some(arg) = args.next() { - if arg == extern_flag { + if arg == "--extern" { // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files. forward_patched_extern_arg(&mut args, &mut cmd); - } else if arg == runtool_flag { + } else if arg == "--runtool" { // An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support. // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag; // otherwise, we won't be called as rustdoc at all. diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 66f323290b..58deac6656 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -479,10 +479,11 @@ impl Command { Ok(()) } - fn run(dep: bool, flags: Vec) -> Result<()> { + fn run(dep: bool, mut flags: Vec) -> Result<()> { let mut e = MiriEnv::new()?; // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so - // that we set the MIRI_SYSROOT up the right way. + // that we set the MIRI_SYSROOT up the right way. We must make sure that + // MIRI_TEST_TARGET and `--target` are in sync. use itertools::Itertools; let target = flags .iter() @@ -493,33 +494,35 @@ impl Command { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { - // Make sure miri actually uses this target. - let miriflags = e.sh.var("MIRIFLAGS").unwrap_or_default(); - e.sh.set_var("MIRIFLAGS", format!("{miriflags} --target {target}")); + // Convert `MIRI_TEST_TARGET` into `--target`. + flags.push("--target".into()); + flags.push(target.into()); } - // Scan for "--edition" (we'll set one ourselves if that flag is not present). + // Scan for "--edition", set one ourselves if that flag is not present. let have_edition = flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition"); + if !have_edition { + flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` + } // Prepare a sysroot. e.build_miri_sysroot(/* quiet */ true)?; - // Then run the actual command. + // Then run the actual command. Also add MIRIFLAGS. let miri_manifest = path!(e.miri_dir / "Cargo.toml"); let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default(); let miri_flags = flagsplit(&miri_flags); let toolchain = &e.toolchain; let extra_flags = &e.cargo_extra_flags; - let edition_flags = (!have_edition).then_some("--edition=2021"); // keep in sync with `tests/ui.rs`.` if dep { cmd!( e.sh, - "cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {edition_flags...} {flags...}" + "cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {flags...}" ).quiet().run()?; } else { cmd!( e.sh, - "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {edition_flags...} {flags...}" + "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}" ).quiet().run()?; } Ok(()) diff --git a/tests/ui.rs b/tests/ui.rs index 129d1dfd73..a75fa4cf98 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -54,34 +54,13 @@ fn build_so_for_c_ffi_tests() -> PathBuf { so_file_path } -fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config { +/// Does *not* set any args or env vars, since it is shared between the test runner and +/// run_dep_mode. +fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config { // Miri is rustc-like, so we create a default builder for rustc and modify it let mut program = CommandBuilder::rustc(); program.program = miri_path(); - // Add some flags we always want. - program.args.push("-Dwarnings".into()); - program.args.push("-Dunused".into()); - program.args.push("-Ainternal_features".into()); - if let Ok(extra_flags) = env::var("MIRIFLAGS") { - for flag in extra_flags.split_whitespace() { - program.args.push(flag.into()); - } - } - program.args.push("-Zui-testing".into()); - program.args.push("--target".into()); - program.args.push(target.into()); - - // If we're on linux, and we're testing the extern-so functionality, - // then build the shared object file for testing external C function calls - // and push the relevant compiler flag. - if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") { - let so_file_path = build_so_for_c_ffi_tests(); - let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); - flag.push(so_file_path.into_os_string()); - program.args.push(flag); - } - let mut config = Config { target: Some(target.to_owned()), stderr_filters: STDERR.clone(), @@ -119,17 +98,38 @@ fn run_tests( with_dependencies: bool, tmpdir: &Path, ) -> Result<()> { - let mut config = test_config(target, path, mode, with_dependencies); + let mut config = miri_config(target, path, mode, with_dependencies); // Add a test env var to do environment communication tests. config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into()))); - // Let the tests know where to store temp files (they might run for a different target, which can make this hard to find). config.program.envs.push(("MIRI_TEMP".into(), Some(tmpdir.to_owned().into()))); - // If a test ICEs, we want to see a backtrace. config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into()))); + // Add some flags we always want. + config.program.args.push("-Dwarnings".into()); + config.program.args.push("-Dunused".into()); + config.program.args.push("-Ainternal_features".into()); + if let Ok(extra_flags) = env::var("MIRIFLAGS") { + for flag in extra_flags.split_whitespace() { + config.program.args.push(flag.into()); + } + } + config.program.args.push("-Zui-testing".into()); + config.program.args.push("--target".into()); + config.program.args.push(target.into()); + + // If we're on linux, and we're testing the extern-so functionality, + // then build the shared object file for testing external C function calls + // and push the relevant compiler flag. + if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") { + let so_file_path = build_so_for_c_ffi_tests(); + let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); + flag.push(so_file_path.into_os_string()); + config.program.args.push(flag); + } + // Handle command-line arguments. let args = ui_test::Args::test()?; let default_bless = env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); @@ -292,13 +292,12 @@ fn main() -> Result<()> { fn run_dep_mode(target: String, mut args: impl Iterator) -> Result<()> { let path = args.next().expect("./miri run-dep must be followed by a file name"); - let mut config = test_config( + let config = miri_config( &target, "", Mode::Yolo { rustfix: RustfixMode::Disabled }, /* with dependencies */ true, ); - config.program.args.clear(); // We want to give the user full control over flags let dep_args = config.build_dependencies()?; let mut cmd = config.program.build(&config.out_dir);