Skip to content

Commit a719c05

Browse files
committed
Auto merge of rust-lang#2426 - saethlin:unix-exec, r=RalfJung
Use real exec on cfg(unix) targets Closes rust-lang/miri#2421 The standard library has a platform extension trait that lets us get the behavior we want on cfg(unix), so why not use it? I tried this out and it produces the correct behavior in concert with nextest.
2 parents 530abac + 622613f commit a719c05

File tree

1 file changed

+61
-23
lines changed

1 file changed

+61
-23
lines changed

cargo-miri/bin.rs

+61-23
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ enum MiriCommand {
5151
}
5252

5353
/// The information to run a crate with the given environment.
54-
#[derive(Serialize, Deserialize)]
54+
#[derive(Clone, Serialize, Deserialize)]
5555
struct CrateRunEnv {
5656
/// The command-line arguments.
5757
args: Vec<String>,
@@ -250,27 +250,56 @@ fn xargo_check() -> Command {
250250
Command::new(env::var_os("XARGO_CHECK").unwrap_or_else(|| OsString::from("xargo-check")))
251251
}
252252

253-
/// Execute the command. If it fails, fail this process with the same exit code.
254-
/// Otherwise, continue.
255-
fn exec(mut cmd: Command) {
256-
let exit_status = cmd.status().expect("failed to run command");
257-
if exit_status.success().not() {
253+
/// Execute the `Command`, where possible by replacing the current process with a new process
254+
/// described by the `Command`. Then exit this process with the exit code of the new process.
255+
fn exec(mut cmd: Command) -> ! {
256+
// On non-Unix imitate POSIX exec as closely as we can
257+
#[cfg(not(unix))]
258+
{
259+
let exit_status = cmd.status().expect("failed to run command");
258260
std::process::exit(exit_status.code().unwrap_or(-1))
259261
}
262+
// On Unix targets, actually exec.
263+
// If exec returns, process setup has failed. This is the same error condition as the expect in
264+
// the non-Unix case.
265+
#[cfg(unix)]
266+
{
267+
use std::os::unix::process::CommandExt;
268+
let error = cmd.exec();
269+
Err(error).expect("failed to run command")
270+
}
260271
}
261272

262-
/// Execute the command and pipe `input` into its stdin.
263-
/// If it fails, fail this process with the same exit code.
264-
/// Otherwise, continue.
265-
fn exec_with_pipe(mut cmd: Command, input: &[u8]) {
266-
cmd.stdin(process::Stdio::piped());
267-
let mut child = cmd.spawn().expect("failed to spawn process");
273+
/// Execute the `Command`, where possible by replacing the current process with a new process
274+
/// described by the `Command`. Then exit this process with the exit code of the new process.
275+
/// `input` is also piped to the new process's stdin, on cfg(unix) platforms by writing its
276+
/// contents to `path` first, then setting stdin to that file.
277+
fn exec_with_pipe<P>(mut cmd: Command, input: &[u8], path: P) -> !
278+
where
279+
P: AsRef<Path>,
280+
{
281+
#[cfg(unix)]
268282
{
269-
let stdin = child.stdin.as_mut().expect("failed to open stdin");
270-
stdin.write_all(input).expect("failed to write out test source");
283+
// Write the bytes we want to send to stdin out to a file
284+
std::fs::write(&path, input).unwrap();
285+
// Open the file for reading, and set our new stdin to it
286+
let stdin = File::open(&path).unwrap();
287+
cmd.stdin(stdin);
288+
// Unlink the file so that it is fully cleaned up as soon as the new process exits
289+
std::fs::remove_file(&path).unwrap();
290+
// Finally, we can hand off control.
291+
exec(cmd)
271292
}
272-
let exit_status = child.wait().expect("failed to run command");
273-
if exit_status.success().not() {
293+
#[cfg(not(unix))]
294+
{
295+
drop(path); // We don't need the path, we can pipe the bytes directly
296+
cmd.stdin(process::Stdio::piped());
297+
let mut child = cmd.spawn().expect("failed to spawn process");
298+
{
299+
let stdin = child.stdin.as_mut().expect("failed to open stdin");
300+
stdin.write_all(input).expect("failed to write out test source");
301+
}
302+
let exit_status = child.wait().expect("failed to run command");
274303
std::process::exit(exit_status.code().unwrap_or(-1))
275304
}
276305
}
@@ -890,6 +919,8 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
890919
// and environment variables; this is used when cargo calls us again in the CARGO_TARGET_RUNNER phase.
891920
let env = CrateRunEnv::collect(args, inside_rustdoc);
892921

922+
store_json(CrateRunInfo::RunWith(env.clone()));
923+
893924
// Rustdoc expects us to exit with an error code if the test is marked as `compile_fail`,
894925
// just creating the JSON file is not enough: we need to detect syntax errors,
895926
// so we need to run Miri with `MIRI_BE_RUSTC` for a check-only build.
@@ -906,7 +937,15 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
906937
cmd.arg("--emit=metadata");
907938
}
908939

909-
cmd.args(&env.args);
940+
// Alter the `-o` parameter so that it does not overwrite the JSON file we stored above.
941+
let mut args = env.args.clone();
942+
for i in 0..args.len() {
943+
if args[i] == "-o" {
944+
args[i + 1].push_str(".miri");
945+
}
946+
}
947+
948+
cmd.args(&args);
910949
cmd.env("MIRI_BE_RUSTC", "target");
911950

912951
if verbose > 0 {
@@ -917,11 +956,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
917956
eprintln!("[cargo-miri rustc inside rustdoc] going to run:\n{:?}", cmd);
918957
}
919958

920-
exec_with_pipe(cmd, &env.stdin);
959+
exec_with_pipe(cmd, &env.stdin, format!("{}.stdin", out_filename("", "").display()));
921960
}
922961

923-
store_json(CrateRunInfo::RunWith(env));
924-
925962
return;
926963
}
927964

@@ -997,8 +1034,6 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
9971034
"[cargo-miri rustc] target_crate={target_crate} runnable_crate={runnable_crate} info_query={info_query}"
9981035
);
9991036
}
1000-
debug_cmd("[cargo-miri rustc]", verbose, &cmd);
1001-
exec(cmd);
10021037

10031038
// Create a stub .rlib file if "link" was requested by cargo.
10041039
// This is necessary to prevent cargo from doing rebuilds all the time.
@@ -1013,6 +1048,9 @@ fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
10131048
File::create(out_filename("", ".dll")).expect("failed to create fake .dll file");
10141049
File::create(out_filename("", ".lib")).expect("failed to create fake .lib file");
10151050
}
1051+
1052+
debug_cmd("[cargo-miri rustc]", verbose, &cmd);
1053+
exec(cmd);
10161054
}
10171055

10181056
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -1117,7 +1155,7 @@ fn phase_runner(mut binary_args: impl Iterator<Item = String>, phase: RunnerPhas
11171155
// Run it.
11181156
debug_cmd("[cargo-miri runner]", verbose, &cmd);
11191157
match phase {
1120-
RunnerPhase::Rustdoc => exec_with_pipe(cmd, &info.stdin),
1158+
RunnerPhase::Rustdoc => exec_with_pipe(cmd, &info.stdin, format!("{}.stdin", binary)),
11211159
RunnerPhase::Cargo => exec(cmd),
11221160
}
11231161
}

0 commit comments

Comments
 (0)