Skip to content

Commit f3ff04b

Browse files
committed
Pull process_builder retry logic into status/output/spawn_with
1 parent 563fdf8 commit f3ff04b

File tree

1 file changed

+46
-35
lines changed

1 file changed

+46
-35
lines changed

src/cargo/util/process_builder.rs

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use std::ffi::{OsStr, OsString};
44
use std::fmt;
55
use std::io::Write;
66
use std::path::Path;
7-
use std::process::{Command, ExitStatus, Output, Stdio};
7+
use std::process::{Child, Command, ExitStatus, Output, Stdio};
88

99
use failure::Fail;
1010
use jobserver::Client;
1111
use shell_escape::escape;
12+
use tempfile::{NamedTempFile, TempPath};
1213

1314
use crate::util::{process_error, read2, CargoResult, CargoResultExt};
1415

@@ -152,13 +153,9 @@ impl ProcessBuilder {
152153

153154
/// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
154155
pub fn exec(&self) -> CargoResult<()> {
155-
let exit = match self.build_command().status() {
156-
Err(ref err) if imp::command_line_too_big(err) => self
157-
.build_command_and_response_file()
158-
.and_then(|(mut cmd, _file)| cmd.status().map_err(|e| e.into())),
159-
other => other.map_err(|e| e.into()),
160-
}
161-
.chain_err(|| process_error(&format!("could not execute process {}", self), None, None))?;
156+
let exit = self.status().chain_err(|| {
157+
process_error(&format!("could not execute process {}", self), None, None)
158+
})?;
162159

163160
if exit.success() {
164161
Ok(())
@@ -193,13 +190,9 @@ impl ProcessBuilder {
193190

194191
/// Executes the process, returning the stdio output, or an error if non-zero exit status.
195192
pub fn exec_with_output(&self) -> CargoResult<Output> {
196-
let output = match self.build_command().output() {
197-
Err(ref err) if imp::command_line_too_big(err) => self
198-
.build_command_and_response_file()
199-
.and_then(|(mut cmd, _file)| cmd.output().map_err(|e| e.into())),
200-
other => other.map_err(|e| e.into()),
201-
}
202-
.chain_err(|| process_error(&format!("could not execute process {}", self), None, None))?;
193+
let output = self.output().chain_err(|| {
194+
process_error(&format!("could not execute process {}", self), None, None)
195+
})?;
203196

204197
if output.status.success() {
205198
Ok(output)
@@ -231,26 +224,13 @@ impl ProcessBuilder {
231224
let mut stdout = Vec::new();
232225
let mut stderr = Vec::new();
233226

234-
let mut cmd = self.build_command();
235-
cmd.stdout(Stdio::piped())
236-
.stderr(Stdio::piped())
237-
.stdin(Stdio::null());
238-
239227
let mut callback_error = None;
240228
let status = (|| -> CargoResult<ExitStatus> {
241-
let mut response_file = None;
242-
let mut child = match cmd.spawn() {
243-
Err(ref err) if imp::command_line_too_big(err) => self
244-
.build_command_and_response_file()
245-
.and_then(|(mut cmd, file)| {
246-
cmd.stdout(Stdio::piped())
247-
.stderr(Stdio::piped())
248-
.stdin(Stdio::null());
249-
response_file = Some(file);
250-
Ok(cmd.spawn()?)
251-
}),
252-
other => other.map_err(|e| e.into()),
253-
}?;
229+
let (mut child, _response_file) = self.spawn_with(|cmd| {
230+
cmd.stdout(Stdio::piped())
231+
.stderr(Stdio::piped())
232+
.stdin(Stdio::null());
233+
})?;
254234
let out = child.stdout.take().unwrap();
255235
let err = child.stderr.take().unwrap();
256236
read2(out, err, &mut |is_out, data, eof| {
@@ -350,7 +330,7 @@ impl ProcessBuilder {
350330

351331
/// Converts `ProcessBuilder` into a `std::process::Command` and a rustc style response
352332
/// file. Also handles the jobserver, if present.
353-
pub fn build_command_and_response_file(&self) -> CargoResult<(Command, tempfile::TempPath)> {
333+
pub fn build_command_and_response_file(&self) -> CargoResult<(Command, TempPath)> {
354334
// The rust linker also jumps through similar hoops, although with a different
355335
// of response file, which this borrows from. Some references:
356336
// https://github.com/rust-lang/rust/blob/ef92009c1dbe2750f1d24a6619b827721fb49749/src/librustc_codegen_ssa/back/link.rs#L935
@@ -362,7 +342,7 @@ impl ProcessBuilder {
362342
}
363343
// cmd.exe can handle up to 8k work of args, this leaves some headroom if using a .cmd rustc wrapper.
364344
let mut cmd_remaining: usize = 1024 * 6;
365-
let mut response_file = tempfile::NamedTempFile::new()?;
345+
let mut response_file = NamedTempFile::new()?;
366346
for arg in &self.args {
367347
cmd_remaining = cmd_remaining.saturating_sub(arg.len());
368348
if cmd_remaining > 0 {
@@ -391,6 +371,37 @@ impl ProcessBuilder {
391371
}
392372
Ok((command, response_file.into_temp_path()))
393373
}
374+
375+
fn status(&self) -> CargoResult<ExitStatus> {
376+
let (mut child, _response_file) = self.spawn_with(|_cmd| {})?;
377+
Ok(child.wait()?)
378+
}
379+
380+
fn output(&self) -> CargoResult<Output> {
381+
let (child, _response_file) = self.spawn_with(|cmd| {
382+
cmd.stdout(Stdio::piped())
383+
.stderr(Stdio::piped())
384+
.stdin(Stdio::null());
385+
})?;
386+
Ok(child.wait_with_output()?)
387+
}
388+
389+
fn spawn_with(
390+
&self,
391+
mut modify_command: impl FnMut(&mut Command),
392+
) -> CargoResult<(Child, Option<TempPath>)> {
393+
let mut command = self.build_command();
394+
modify_command(&mut command);
395+
match command.spawn() {
396+
Ok(child) => Ok((child, None)),
397+
Err(ref err) if imp::command_line_too_big(err) => {
398+
let (mut command, response_file) = self.build_command_and_response_file()?;
399+
modify_command(&mut command);
400+
Ok((command.spawn()?, Some(response_file)))
401+
}
402+
Err(other) => Err(other.into()),
403+
}
404+
}
394405
}
395406

396407
/// A helper function to create a `ProcessBuilder`.

0 commit comments

Comments
 (0)