Move ProcessBuilder to cargo-util.

This commit is contained in:
Eric Huss 2021-03-20 11:28:38 -07:00
parent 669340333a
commit 888100352a
36 changed files with 347 additions and 294 deletions

View File

@ -66,6 +66,7 @@ jobs:
- run: cargo test --features 'deny-warnings' - run: cargo test --features 'deny-warnings'
- run: cargo test --features 'deny-warnings' -p cargo-test-support - run: cargo test --features 'deny-warnings' -p cargo-test-support
- run: cargo test -p cargo-platform - run: cargo test -p cargo-platform
- run: cargo test -p cargo-util
- run: cargo test --manifest-path crates/mdman/Cargo.toml - run: cargo test --manifest-path crates/mdman/Cargo.toml
- run: cargo build --manifest-path crates/credential/cargo-credential-1password/Cargo.toml - run: cargo build --manifest-path crates/credential/cargo-credential-1password/Cargo.toml
- run: cargo build --manifest-path crates/credential/cargo-credential-gnome-secret/Cargo.toml - run: cargo build --manifest-path crates/credential/cargo-credential-gnome-secret/Cargo.toml

View File

@ -56,7 +56,6 @@ semver = { version = "0.10", features = ["serde"] }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_ignored = "0.1.0" serde_ignored = "0.1.0"
serde_json = { version = "1.0.30", features = ["raw_value"] } serde_json = { version = "1.0.30", features = ["raw_value"] }
shell-escape = "0.1.4"
strip-ansi-escapes = "0.1.0" strip-ansi-escapes = "0.1.0"
tar = { version = "0.4.26", default-features = false } tar = { version = "0.4.26", default-features = false }
tempfile = "3.0" tempfile = "3.0"
@ -80,7 +79,6 @@ rand = "0.8.3"
core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] } core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
miow = "0.3.6"
fwdansi = "1.1.0" fwdansi = "1.1.0"
[target.'cfg(windows)'.dependencies.winapi] [target.'cfg(windows)'.dependencies.winapi]

View File

@ -11,6 +11,7 @@ doctest = false
[dependencies] [dependencies]
cargo = { path = "../.." } cargo = { path = "../.." }
cargo-test-macro = { path = "../cargo-test-macro" } cargo-test-macro = { path = "../cargo-test-macro" }
cargo-util = { path = "../cargo-util" }
filetime = "0.2" filetime = "0.2"
flate2 = { version = "1.0", default-features = false, features = ["zlib"] } flate2 = { version = "1.0", default-features = false, features = ["zlib"] }
git2 = "0.13.16" git2 = "0.13.16"

View File

@ -10,8 +10,8 @@
//! These tests are all disabled on rust-lang/rust's CI, but run in Cargo's CI. //! These tests are all disabled on rust-lang/rust's CI, but run in Cargo's CI.
use crate::{basic_manifest, main_file, project}; use crate::{basic_manifest, main_file, project};
use cargo::util::ProcessError;
use cargo::CargoResult; use cargo::CargoResult;
use cargo_util::ProcessError;
use std::env; use std::env;
use std::fmt::Write; use std::fmt::Write;
use std::process::{Command, Output}; use std::process::{Command, Output};

View File

@ -15,7 +15,8 @@ use std::process::{Command, Output};
use std::str; use std::str;
use std::time::{self, Duration}; use std::time::{self, Duration};
use cargo::util::{is_ci, CargoResult, ProcessBuilder, ProcessError, Rustc}; use cargo::util::{is_ci, CargoResult, Rustc};
use cargo_util::{ProcessBuilder, ProcessError};
use serde_json::{self, Value}; use serde_json::{self, Value};
use url::Url; use url::Url;
@ -1569,12 +1570,12 @@ pub fn is_nightly() -> bool {
.with(|r| r.verbose_version.contains("-nightly") || r.verbose_version.contains("-dev")) .with(|r| r.verbose_version.contains("-nightly") || r.verbose_version.contains("-dev"))
} }
pub fn process<T: AsRef<OsStr>>(t: T) -> cargo::util::ProcessBuilder { pub fn process<T: AsRef<OsStr>>(t: T) -> ProcessBuilder {
_process(t.as_ref()) _process(t.as_ref())
} }
fn _process(t: &OsStr) -> cargo::util::ProcessBuilder { fn _process(t: &OsStr) -> ProcessBuilder {
let mut p = cargo::util::process(t); let mut p = ProcessBuilder::new(t);
// In general just clear out all cargo-specific configuration already in the // In general just clear out all cargo-specific configuration already in the
// environment. Our tests all assume a "default configuration" unless // environment. Our tests all assume a "default configuration" unless
@ -1643,7 +1644,7 @@ pub trait ChannelChanger: Sized {
fn masquerade_as_nightly_cargo(&mut self) -> &mut Self; fn masquerade_as_nightly_cargo(&mut self) -> &mut Self;
} }
impl ChannelChanger for cargo::util::ProcessBuilder { impl ChannelChanger for ProcessBuilder {
fn masquerade_as_nightly_cargo(&mut self) -> &mut Self { fn masquerade_as_nightly_cargo(&mut self) -> &mut Self {
self.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly") self.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly")
} }

View File

@ -9,3 +9,11 @@ repository = "https://github.com/rust-lang/cargo"
description = "Miscellaneous support code used by Cargo." description = "Miscellaneous support code used by Cargo."
[dependencies] [dependencies]
anyhow = "1.0"
jobserver = "0.1.21"
libc = "0.2"
shell-escape = "0.1.4"
[target.'cfg(windows)'.dependencies]
miow = "0.3.6"
winapi = { version = "0.3", features = ["consoleapi", "minwindef"] }

View File

@ -0,0 +1 @@
../../LICENSE-APACHE

View File

@ -0,0 +1 @@
../../LICENSE-MIT

View File

@ -1 +1,9 @@
//! Miscellaneous support code used by Cargo. //! Miscellaneous support code used by Cargo.
pub use self::read2::read2;
pub use process_builder::ProcessBuilder;
pub use process_error::{exit_status_to_string, is_simple_exit_code, ProcessError};
mod process_builder;
mod process_error;
mod read2;

View File

@ -1,5 +1,6 @@
use crate::util::{process_error, read2, CargoResult, CargoResultExt}; use crate::process_error::ProcessError;
use anyhow::bail; use crate::read2;
use anyhow::{bail, Context, Result};
use jobserver::Client; use jobserver::Client;
use shell_escape::escape; use shell_escape::escape;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -10,7 +11,7 @@ use std::iter::once;
use std::path::Path; use std::path::Path;
use std::process::{Command, Output, Stdio}; use std::process::{Command, Output, Stdio};
/// A builder object for an external process, similar to `std::process::Command`. /// A builder object for an external process, similar to [`std::process::Command`].
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ProcessBuilder { pub struct ProcessBuilder {
/// The program to execute. /// The program to execute.
@ -21,10 +22,10 @@ pub struct ProcessBuilder {
env: BTreeMap<String, Option<OsString>>, env: BTreeMap<String, Option<OsString>>,
/// The directory to run the program from. /// The directory to run the program from.
cwd: Option<OsString>, cwd: Option<OsString>,
/// The `make` jobserver. See the [jobserver crate][jobserver_docs] for /// The `make` jobserver. See the [jobserver crate] for
/// more information. /// more information.
/// ///
/// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/ /// [jobserver crate]: https://docs.rs/jobserver/
jobserver: Option<Client>, jobserver: Option<Client>,
/// `true` to include environment variable in display. /// `true` to include environment variable in display.
display_env_vars: bool, display_env_vars: bool,
@ -58,6 +59,18 @@ impl fmt::Display for ProcessBuilder {
} }
impl ProcessBuilder { impl ProcessBuilder {
/// Creates a new [`ProcessBuilder`] with the given executable path.
pub fn new<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
ProcessBuilder {
program: cmd.as_ref().to_os_string(),
args: Vec::new(),
cwd: None,
env: BTreeMap::new(),
jobserver: None,
display_env_vars: false,
}
}
/// (chainable) Sets the executable for the process. /// (chainable) Sets the executable for the process.
pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder { pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder {
self.program = program.as_ref().to_os_string(); self.program = program.as_ref().to_os_string();
@ -149,16 +162,16 @@ impl ProcessBuilder {
} }
/// Runs the process, waiting for completion, and mapping non-success exit codes to an error. /// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
pub fn exec(&self) -> CargoResult<()> { pub fn exec(&self) -> Result<()> {
let mut command = self.build_command(); let mut command = self.build_command();
let exit = command.status().chain_err(|| { let exit = command.status().with_context(|| {
process_error(&format!("could not execute process {}", self), None, None) ProcessError::new(&format!("could not execute process {}", self), None, None)
})?; })?;
if exit.success() { if exit.success() {
Ok(()) Ok(())
} else { } else {
Err(process_error( Err(ProcessError::new(
&format!("process didn't exit successfully: {}", self), &format!("process didn't exit successfully: {}", self),
Some(exit), Some(exit),
None, None,
@ -182,22 +195,22 @@ impl ProcessBuilder {
/// include our child process. If the child terminates then we'll reap them in Cargo /// include our child process. If the child terminates then we'll reap them in Cargo
/// pretty quickly, and if the child handles the signal then we won't terminate /// pretty quickly, and if the child handles the signal then we won't terminate
/// (and we shouldn't!) until the process itself later exits. /// (and we shouldn't!) until the process itself later exits.
pub fn exec_replace(&self) -> CargoResult<()> { pub fn exec_replace(&self) -> Result<()> {
imp::exec_replace(self) imp::exec_replace(self)
} }
/// Executes the process, returning the stdio output, or an error if non-zero exit status. /// Executes the process, returning the stdio output, or an error if non-zero exit status.
pub fn exec_with_output(&self) -> CargoResult<Output> { pub fn exec_with_output(&self) -> Result<Output> {
let mut command = self.build_command(); let mut command = self.build_command();
let output = command.output().chain_err(|| { let output = command.output().with_context(|| {
process_error(&format!("could not execute process {}", self), None, None) ProcessError::new(&format!("could not execute process {}", self), None, None)
})?; })?;
if output.status.success() { if output.status.success() {
Ok(output) Ok(output)
} else { } else {
Err(process_error( Err(ProcessError::new(
&format!("process didn't exit successfully: {}", self), &format!("process didn't exit successfully: {}", self),
Some(output.status), Some(output.status),
Some(&output), Some(&output),
@ -217,10 +230,10 @@ impl ProcessBuilder {
/// output. /// output.
pub fn exec_with_streaming( pub fn exec_with_streaming(
&self, &self,
on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>, on_stdout_line: &mut dyn FnMut(&str) -> Result<()>,
on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>, on_stderr_line: &mut dyn FnMut(&str) -> Result<()>,
capture_output: bool, capture_output: bool,
) -> CargoResult<Output> { ) -> Result<Output> {
let mut stdout = Vec::new(); let mut stdout = Vec::new();
let mut stderr = Vec::new(); let mut stderr = Vec::new();
@ -274,7 +287,9 @@ impl ProcessBuilder {
})?; })?;
child.wait() child.wait()
})() })()
.chain_err(|| process_error(&format!("could not execute process {}", self), None, None))?; .with_context(|| {
ProcessError::new(&format!("could not execute process {}", self), None, None)
})?;
let output = Output { let output = Output {
status, status,
stdout, stdout,
@ -284,14 +299,14 @@ impl ProcessBuilder {
{ {
let to_print = if capture_output { Some(&output) } else { None }; let to_print = if capture_output { Some(&output) } else { None };
if let Some(e) = callback_error { if let Some(e) = callback_error {
let cx = process_error( let cx = ProcessError::new(
&format!("failed to parse process output: {}", self), &format!("failed to parse process output: {}", self),
Some(output.status), Some(output.status),
to_print, to_print,
); );
bail!(anyhow::Error::new(cx).context(e)); bail!(anyhow::Error::new(cx).context(e));
} else if !output.status.success() { } else if !output.status.success() {
bail!(process_error( bail!(ProcessError::new(
&format!("process didn't exit successfully: {}", self), &format!("process didn't exit successfully: {}", self),
Some(output.status), Some(output.status),
to_print, to_print,
@ -333,9 +348,9 @@ impl ProcessBuilder {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use cargo::util::{ProcessBuilder, process}; /// use cargo_util::ProcessBuilder;
/// // Running this would execute `rustc` /// // Running this would execute `rustc`
/// let cmd: ProcessBuilder = process("rustc"); /// let cmd = ProcessBuilder::new("rustc");
/// ///
/// // Running this will execute `sccache rustc` /// // Running this will execute `sccache rustc`
/// let cmd = cmd.wrapped(Some("sccache")); /// let cmd = cmd.wrapped(Some("sccache"));
@ -360,28 +375,16 @@ impl ProcessBuilder {
} }
} }
/// A helper function to create a `ProcessBuilder`.
pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
ProcessBuilder {
program: cmd.as_ref().to_os_string(),
args: Vec::new(),
cwd: None,
env: BTreeMap::new(),
jobserver: None,
display_env_vars: false,
}
}
#[cfg(unix)] #[cfg(unix)]
mod imp { mod imp {
use crate::util::{process_error, ProcessBuilder}; use super::{ProcessBuilder, ProcessError};
use crate::CargoResult; use anyhow::Result;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> { pub fn exec_replace(process_builder: &ProcessBuilder) -> Result<()> {
let mut command = process_builder.build_command(); let mut command = process_builder.build_command();
let error = command.exec(); let error = command.exec();
Err(anyhow::Error::from(error).context(process_error( Err(anyhow::Error::from(error).context(ProcessError::new(
&format!("could not execute process {}", process_builder), &format!("could not execute process {}", process_builder),
None, None,
None, None,
@ -391,8 +394,8 @@ mod imp {
#[cfg(windows)] #[cfg(windows)]
mod imp { mod imp {
use crate::util::{process_error, ProcessBuilder}; use super::{ProcessBuilder, ProcessError};
use crate::CargoResult; use anyhow::Result;
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE}; use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
use winapi::um::consoleapi::SetConsoleCtrlHandler; use winapi::um::consoleapi::SetConsoleCtrlHandler;
@ -401,10 +404,10 @@ mod imp {
TRUE TRUE
} }
pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> { pub fn exec_replace(process_builder: &ProcessBuilder) -> Result<()> {
unsafe { unsafe {
if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE { if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE {
return Err(process_error("Could not set Ctrl-C handler.", None, None).into()); return Err(ProcessError::new("Could not set Ctrl-C handler.", None, None).into());
} }
} }

View File

@ -0,0 +1,194 @@
//! Error value for [`crate::ProcessBuilder`] when a process fails.
use std::fmt;
use std::process::{ExitStatus, Output};
use std::str;
#[derive(Debug)]
pub struct ProcessError {
/// A detailed description to show to the user why the process failed.
pub desc: String,
/// The exit status of the process.
///
/// This can be `None` if the process failed to launch (like process not
/// found) or if the exit status wasn't a code but was instead something
/// like termination via a signal.
pub code: Option<i32>,
/// The stdout from the process.
///
/// This can be `None` if the process failed to launch, or the output was
/// not captured.
pub stdout: Option<Vec<u8>>,
/// The stderr from the process.
///
/// This can be `None` if the process failed to launch, or the output was
/// not captured.
pub stderr: Option<Vec<u8>>,
}
impl fmt::Display for ProcessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.desc.fmt(f)
}
}
impl std::error::Error for ProcessError {}
impl ProcessError {
/// Creates a new [`ProcessError`].
///
/// * `status` can be `None` if the process did not launch.
/// * `output` can be `None` if the process did not launch, or output was not captured.
pub fn new(msg: &str, status: Option<ExitStatus>, output: Option<&Output>) -> ProcessError {
let exit = match status {
Some(s) => exit_status_to_string(s),
None => "never executed".to_string(),
};
Self::new_raw(
msg,
status.and_then(|s| s.code()),
&exit,
output.map(|s| s.stdout.as_slice()),
output.map(|s| s.stderr.as_slice()),
)
}
/// Creates a new [`ProcessError`] with the raw output data.
///
/// * `code` can be `None` for situations like being killed by a signal on unix.
pub fn new_raw(
msg: &str,
code: Option<i32>,
status: &str,
stdout: Option<&[u8]>,
stderr: Option<&[u8]>,
) -> ProcessError {
let mut desc = format!("{} ({})", msg, status);
if let Some(out) = stdout {
match str::from_utf8(out) {
Ok(s) if !s.trim().is_empty() => {
desc.push_str("\n--- stdout\n");
desc.push_str(s);
}
Ok(..) | Err(..) => {}
}
}
if let Some(out) = stderr {
match str::from_utf8(out) {
Ok(s) if !s.trim().is_empty() => {
desc.push_str("\n--- stderr\n");
desc.push_str(s);
}
Ok(..) | Err(..) => {}
}
}
ProcessError {
desc,
code,
stdout: stdout.map(|s| s.to_vec()),
stderr: stderr.map(|s| s.to_vec()),
}
}
}
/// Converts an [`ExitStatus`] to a human-readable string suitable for
/// displaying to a user.
pub fn exit_status_to_string(status: ExitStatus) -> String {
return status_to_string(status);
#[cfg(unix)]
fn status_to_string(status: ExitStatus) -> String {
use std::os::unix::process::*;
if let Some(signal) = status.signal() {
let name = match signal as libc::c_int {
libc::SIGABRT => ", SIGABRT: process abort signal",
libc::SIGALRM => ", SIGALRM: alarm clock",
libc::SIGFPE => ", SIGFPE: erroneous arithmetic operation",
libc::SIGHUP => ", SIGHUP: hangup",
libc::SIGILL => ", SIGILL: illegal instruction",
libc::SIGINT => ", SIGINT: terminal interrupt signal",
libc::SIGKILL => ", SIGKILL: kill",
libc::SIGPIPE => ", SIGPIPE: write on a pipe with no one to read",
libc::SIGQUIT => ", SIGQUIT: terminal quit signal",
libc::SIGSEGV => ", SIGSEGV: invalid memory reference",
libc::SIGTERM => ", SIGTERM: termination signal",
libc::SIGBUS => ", SIGBUS: access to undefined memory",
#[cfg(not(target_os = "haiku"))]
libc::SIGSYS => ", SIGSYS: bad system call",
libc::SIGTRAP => ", SIGTRAP: trace/breakpoint trap",
_ => "",
};
format!("signal: {}{}", signal, name)
} else {
status.to_string()
}
}
#[cfg(windows)]
fn status_to_string(status: ExitStatus) -> String {
use winapi::shared::minwindef::DWORD;
use winapi::um::winnt::*;
let mut base = status.to_string();
let extra = match status.code().unwrap() as DWORD {
STATUS_ACCESS_VIOLATION => "STATUS_ACCESS_VIOLATION",
STATUS_IN_PAGE_ERROR => "STATUS_IN_PAGE_ERROR",
STATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
STATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
STATUS_NO_MEMORY => "STATUS_NO_MEMORY",
STATUS_ILLEGAL_INSTRUCTION => "STATUS_ILLEGAL_INSTRUCTION",
STATUS_NONCONTINUABLE_EXCEPTION => "STATUS_NONCONTINUABLE_EXCEPTION",
STATUS_INVALID_DISPOSITION => "STATUS_INVALID_DISPOSITION",
STATUS_ARRAY_BOUNDS_EXCEEDED => "STATUS_ARRAY_BOUNDS_EXCEEDED",
STATUS_FLOAT_DENORMAL_OPERAND => "STATUS_FLOAT_DENORMAL_OPERAND",
STATUS_FLOAT_DIVIDE_BY_ZERO => "STATUS_FLOAT_DIVIDE_BY_ZERO",
STATUS_FLOAT_INEXACT_RESULT => "STATUS_FLOAT_INEXACT_RESULT",
STATUS_FLOAT_INVALID_OPERATION => "STATUS_FLOAT_INVALID_OPERATION",
STATUS_FLOAT_OVERFLOW => "STATUS_FLOAT_OVERFLOW",
STATUS_FLOAT_STACK_CHECK => "STATUS_FLOAT_STACK_CHECK",
STATUS_FLOAT_UNDERFLOW => "STATUS_FLOAT_UNDERFLOW",
STATUS_INTEGER_DIVIDE_BY_ZERO => "STATUS_INTEGER_DIVIDE_BY_ZERO",
STATUS_INTEGER_OVERFLOW => "STATUS_INTEGER_OVERFLOW",
STATUS_PRIVILEGED_INSTRUCTION => "STATUS_PRIVILEGED_INSTRUCTION",
STATUS_STACK_OVERFLOW => "STATUS_STACK_OVERFLOW",
STATUS_DLL_NOT_FOUND => "STATUS_DLL_NOT_FOUND",
STATUS_ORDINAL_NOT_FOUND => "STATUS_ORDINAL_NOT_FOUND",
STATUS_ENTRYPOINT_NOT_FOUND => "STATUS_ENTRYPOINT_NOT_FOUND",
STATUS_CONTROL_C_EXIT => "STATUS_CONTROL_C_EXIT",
STATUS_DLL_INIT_FAILED => "STATUS_DLL_INIT_FAILED",
STATUS_FLOAT_MULTIPLE_FAULTS => "STATUS_FLOAT_MULTIPLE_FAULTS",
STATUS_FLOAT_MULTIPLE_TRAPS => "STATUS_FLOAT_MULTIPLE_TRAPS",
STATUS_REG_NAT_CONSUMPTION => "STATUS_REG_NAT_CONSUMPTION",
STATUS_HEAP_CORRUPTION => "STATUS_HEAP_CORRUPTION",
STATUS_STACK_BUFFER_OVERRUN => "STATUS_STACK_BUFFER_OVERRUN",
STATUS_ASSERTION_FAILURE => "STATUS_ASSERTION_FAILURE",
_ => return base,
};
base.push_str(", ");
base.push_str(extra);
base
}
}
/// Returns `true` if the given process exit code is something a normal
/// process would exit with.
///
/// This helps differentiate from abnormal termination codes, such as
/// segmentation faults or signals.
pub fn is_simple_exit_code(code: i32) -> bool {
// Typical unix exit codes are 0 to 127.
// Windows doesn't have anything "typical", and is a
// 32-bit number (which appears signed here, but is really
// unsigned). However, most of the interesting NTSTATUS
// codes are very large. This is just a rough
// approximation of which codes are "normal" and which
// ones are abnormal termination.
code >= 0 && code <= 127
}

View File

@ -12,6 +12,7 @@ from urllib.error import HTTPError
TO_PUBLISH = [ TO_PUBLISH = [
'crates/cargo-platform', 'crates/cargo-platform',
'crates/cargo-util',
'crates/crates-io', 'crates/crates-io',
'.', '.',
] ]

View File

@ -1,8 +1,8 @@
use crate::command_prelude::*; use crate::command_prelude::*;
use crate::util::restricted_names::is_glob_pattern; use crate::util::restricted_names::is_glob_pattern;
use crate::util::ProcessError;
use cargo::core::Verbosity; use cargo::core::Verbosity;
use cargo::ops::{self, CompileFilter, Packages}; use cargo::ops::{self, CompileFilter, Packages};
use cargo_util::ProcessError;
pub fn cli() -> App { pub fn cli() -> App {
subcommand("run") subcommand("run")

View File

@ -1,7 +1,6 @@
use crate::command_prelude::*; use crate::command_prelude::*;
use anyhow::Error; use anyhow::Error;
use cargo::ops::{self, CompileFilter, FilterRule, LibRule}; use cargo::ops::{self, CompileFilter, FilterRule, LibRule};
use cargo::util::errors;
pub fn cli() -> App { pub fn cli() -> App {
subcommand("test") subcommand("test")
@ -128,7 +127,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let context = anyhow::format_err!("{}", err.hint(&ws, &ops.compile_opts)); let context = anyhow::format_err!("{}", err.hint(&ws, &ops.compile_opts));
let e = match err.code { let e = match err.code {
// Don't show "process didn't exit successfully" for simple errors. // Don't show "process didn't exit successfully" for simple errors.
Some(i) if errors::is_simple_exit_code(i) => CliError::new(context, i), Some(i) if cargo_util::is_simple_exit_code(i) => CliError::new(context, i),
Some(i) => CliError::new(Error::from(err).context(context), i), Some(i) => CliError::new(Error::from(err).context(context), i),
None => CliError::new(Error::from(err).context(context), 101), None => CliError::new(Error::from(err).context(context), 101),
}; };

View File

@ -3,15 +3,15 @@
#![warn(clippy::needless_borrow)] #![warn(clippy::needless_borrow)]
#![warn(clippy::redundant_clone)] #![warn(clippy::redundant_clone)]
use cargo::core::shell::Shell;
use cargo::util::CliError;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
use cargo_util::{ProcessBuilder, ProcessError};
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::env; use std::env;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use cargo::core::shell::Shell;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
use cargo::util::{CliError, ProcessError};
mod cli; mod cli;
mod commands; mod commands;
@ -159,7 +159,7 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli
}; };
let cargo_exe = config.cargo_exe()?; let cargo_exe = config.cargo_exe()?;
let err = match util::process(&command) let err = match ProcessBuilder::new(&command)
.env(cargo::CARGO_ENV, cargo_exe) .env(cargo::CARGO_ENV, cargo_exe)
.args(args) .args(args)
.exec_replace() .exec_replace()

View File

@ -1,8 +1,8 @@
use crate::core::compiler::CompileKind; use crate::core::compiler::CompileKind;
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::ProcessBuilder;
use crate::util::{CargoResult, Config, RustfixDiagnosticServer}; use crate::util::{CargoResult, Config, RustfixDiagnosticServer};
use anyhow::bail; use anyhow::bail;
use cargo_util::ProcessBuilder;
use serde::ser; use serde::ser;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;

View File

@ -3,8 +3,9 @@ use crate::core::compiler::{
}; };
use crate::core::{Dependency, Target, TargetKind, Workspace}; use crate::core::{Dependency, Target, TargetKind, Workspace};
use crate::util::config::{Config, StringList, TargetConfig}; use crate::util::config::{Config, StringList, TargetConfig};
use crate::util::{paths, CargoResult, CargoResultExt, ProcessBuilder, Rustc}; use crate::util::{paths, CargoResult, CargoResultExt, Rustc};
use cargo_platform::{Cfg, CfgExpr}; use cargo_platform::{Cfg, CfgExpr};
use cargo_util::ProcessBuilder;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap}; use std::collections::hash_map::{Entry, HashMap};

View File

@ -14,7 +14,8 @@ use serde::Serialize;
use super::context::OutputFile; use super::context::OutputFile;
use super::{CompileKind, CompileMode, Context, Unit}; use super::{CompileKind, CompileMode, Context, Unit};
use crate::core::TargetKind; use crate::core::TargetKind;
use crate::util::{internal, CargoResult, Config, ProcessBuilder}; use crate::util::{internal, CargoResult, Config};
use cargo_util::ProcessBuilder;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
struct Invocation { struct Invocation {

View File

@ -4,12 +4,13 @@ use std::ffi::{OsStr, OsString};
use std::path::PathBuf; use std::path::PathBuf;
use cargo_platform::CfgExpr; use cargo_platform::CfgExpr;
use cargo_util::ProcessBuilder;
use semver::Version; use semver::Version;
use super::BuildContext; use super::BuildContext;
use crate::core::compiler::{CompileKind, Metadata, Unit}; use crate::core::compiler::{CompileKind, Metadata, Unit};
use crate::core::Package; use crate::core::Package;
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder}; use crate::util::{self, config, join_paths, CargoResult, Config};
/// Structure with enough information to run `rustdoc --test`. /// Structure with enough information to run `rustdoc --test`.
pub struct Doctest { pub struct Doctest {
@ -184,7 +185,7 @@ impl<'cfg> Compilation<'cfg> {
unit: &Unit, unit: &Unit,
script_meta: Option<Metadata>, script_meta: Option<Metadata>,
) -> CargoResult<ProcessBuilder> { ) -> CargoResult<ProcessBuilder> {
let rustdoc = process(&*self.config.rustdoc()?); let rustdoc = ProcessBuilder::new(&*self.config.rustdoc()?);
let cmd = fill_rustc_tool_env(rustdoc, unit); let cmd = fill_rustc_tool_env(rustdoc, unit);
let mut p = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, true)?; let mut p = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, true)?;
unit.target.edition().cmd_edition_arg(&mut p); unit.target.edition().cmd_edition_arg(&mut p);
@ -207,7 +208,13 @@ impl<'cfg> Compilation<'cfg> {
cmd: T, cmd: T,
pkg: &Package, pkg: &Package,
) -> CargoResult<ProcessBuilder> { ) -> CargoResult<ProcessBuilder> {
self.fill_env(process(cmd), pkg, None, CompileKind::Host, false) self.fill_env(
ProcessBuilder::new(cmd),
pkg,
None,
CompileKind::Host,
false,
)
} }
pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec<String>)> { pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec<String>)> {
@ -229,12 +236,12 @@ impl<'cfg> Compilation<'cfg> {
script_meta: Option<Metadata>, script_meta: Option<Metadata>,
) -> CargoResult<ProcessBuilder> { ) -> CargoResult<ProcessBuilder> {
let builder = if let Some((runner, args)) = self.target_runner(kind) { let builder = if let Some((runner, args)) = self.target_runner(kind) {
let mut builder = process(runner); let mut builder = ProcessBuilder::new(runner);
builder.args(args); builder.args(args);
builder.arg(cmd); builder.arg(cmd);
builder builder
} else { } else {
process(cmd) ProcessBuilder::new(cmd)
}; };
self.fill_env(builder, pkg, script_meta, kind, false) self.fill_env(builder, pkg, script_meta, kind, false)
} }

View File

@ -322,6 +322,7 @@ use std::sync::{Arc, Mutex};
use std::time::SystemTime; use std::time::SystemTime;
use anyhow::{bail, format_err}; use anyhow::{bail, format_err};
use cargo_util::ProcessBuilder;
use filetime::FileTime; use filetime::FileTime;
use log::{debug, info}; use log::{debug, info};
use serde::de; use serde::de;
@ -334,7 +335,7 @@ use crate::util;
use crate::util::errors::{CargoResult, CargoResultExt}; use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::paths; use crate::util::paths;
use crate::util::{internal, path_args, profile, ProcessBuilder}; use crate::util::{internal, path_args, profile};
use super::custom_build::BuildDeps; use super::custom_build::BuildDeps;
use super::job::{Job, Work}; use super::job::{Job, Work};

View File

@ -57,6 +57,7 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use anyhow::format_err; use anyhow::format_err;
use cargo_util::ProcessBuilder;
use crossbeam_utils::thread::Scope; use crossbeam_utils::thread::Scope;
use jobserver::{Acquired, Client, HelperThread}; use jobserver::{Acquired, Client, HelperThread};
use log::{debug, info, trace}; use log::{debug, info, trace};
@ -78,7 +79,7 @@ use crate::drop_eprint;
use crate::util::diagnostic_server::{self, DiagnosticPrinter}; use crate::util::diagnostic_server::{self, DiagnosticPrinter};
use crate::util::machine_message::{self, Message as _}; use crate::util::machine_message::{self, Message as _};
use crate::util::{self, internal, profile}; use crate::util::{self, internal, profile};
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder}; use crate::util::{CargoResult, CargoResultExt};
use crate::util::{Config, DependencyQueue, Progress, ProgressStyle, Queue}; use crate::util::{Config, DependencyQueue, Progress, ProgressStyle, Queue};
/// This structure is backed by the `DependencyQueue` type and manages the /// This structure is backed by the `DependencyQueue` type and manages the

View File

@ -54,11 +54,12 @@ pub use crate::core::compiler::unit::{Unit, UnitInterner};
use crate::core::manifest::TargetSourcePath; use crate::core::manifest::TargetSourcePath;
use crate::core::profiles::{PanicStrategy, Profile, Strip}; use crate::core::profiles::{PanicStrategy, Profile, Strip};
use crate::core::{Feature, PackageId, Target}; use crate::core::{Feature, PackageId, Target};
use crate::util::errors::{self, CargoResult, CargoResultExt, ProcessError, VerboseError}; use crate::util::errors::{CargoResult, CargoResultExt, VerboseError};
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::machine_message::Message; use crate::util::machine_message::Message;
use crate::util::{self, machine_message, ProcessBuilder}; use crate::util::{self, machine_message};
use crate::util::{add_path_args, internal, join_paths, paths, profile}; use crate::util::{add_path_args, internal, join_paths, paths, profile};
use cargo_util::{ProcessBuilder, ProcessError};
const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version"; const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
@ -303,7 +304,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
.as_ref() .as_ref()
.and_then(|perr| perr.code) .and_then(|perr| perr.code)
{ {
Some(n) if errors::is_simple_exit_code(n) => VerboseError::new(err).into(), Some(n) if cargo_util::is_simple_exit_code(n) => VerboseError::new(err).into(),
_ => err, _ => err,
} }
} }

View File

@ -5,7 +5,7 @@ use crate::core::compiler::unit::Unit;
use crate::core::compiler::CompileKind; use crate::core::compiler::CompileKind;
use crate::sources::CRATES_IO_REGISTRY; use crate::sources::CRATES_IO_REGISTRY;
use crate::util::errors::{internal, CargoResult}; use crate::util::errors::{internal, CargoResult};
use crate::util::ProcessBuilder; use cargo_util::ProcessBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::hash; use std::hash;

View File

@ -97,10 +97,11 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use anyhow::{bail, Error}; use anyhow::{bail, Error};
use cargo_util::ProcessBuilder;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::util::errors::CargoResult; use crate::util::errors::CargoResult;
use crate::util::{indented_lines, ProcessBuilder}; use crate::util::indented_lines;
use crate::Config; use crate::Config;
pub const SEE_CHANNELS: &str = pub const SEE_CHANNELS: &str =

View File

@ -1,11 +1,11 @@
use std::ffi::OsString;
use crate::core::compiler::{Compilation, CompileKind, Doctest, UnitOutput}; use crate::core::compiler::{Compilation, CompileKind, Doctest, UnitOutput};
use crate::core::shell::Verbosity; use crate::core::shell::Verbosity;
use crate::core::{TargetKind, Workspace}; use crate::core::{TargetKind, Workspace};
use crate::ops; use crate::ops;
use crate::util::errors::CargoResult; use crate::util::errors::CargoResult;
use crate::util::{add_path_args, CargoTestError, Config, ProcessError, Test}; use crate::util::{add_path_args, CargoTestError, Config, Test};
use cargo_util::ProcessError;
use std::ffi::OsString;
pub struct TestOptions { pub struct TestOptions {
pub compile_opts: ops::CompileOptions, pub compile_opts: ops::CompileOptions,

View File

@ -46,6 +46,7 @@ use std::process::{self, Command, ExitStatus};
use std::str; use std::str;
use anyhow::{bail, Context, Error}; use anyhow::{bail, Context, Error};
use cargo_util::ProcessBuilder;
use log::{debug, trace, warn}; use log::{debug, trace, warn};
use rustfix::diagnostics::Diagnostic; use rustfix::diagnostics::Diagnostic;
use rustfix::{self, CodeFix}; use rustfix::{self, CodeFix};
@ -57,7 +58,7 @@ use crate::core::{Edition, MaybePackage, Workspace};
use crate::ops::{self, CompileOptions}; use crate::ops::{self, CompileOptions};
use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer}; use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer};
use crate::util::errors::CargoResult; use crate::util::errors::CargoResult;
use crate::util::{self, paths, Config, ProcessBuilder}; use crate::util::{self, paths, Config};
use crate::util::{existing_vcs_repo, LockServer, LockServerClient}; use crate::util::{existing_vcs_repo, LockServer, LockServerClient};
use crate::{drop_eprint, drop_eprintln}; use crate::{drop_eprint, drop_eprintln};
@ -84,7 +85,7 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions) -> CargoResult<()> {
// Spin up our lock server, which our subprocesses will use to synchronize fixes. // Spin up our lock server, which our subprocesses will use to synchronize fixes.
let lock_server = LockServer::new()?; let lock_server = LockServer::new()?;
let mut wrapper = util::process(env::current_exe()?); let mut wrapper = ProcessBuilder::new(env::current_exe()?);
wrapper.env(FIX_ENV, lock_server.addr().to_string()); wrapper.env(FIX_ENV, lock_server.addr().to_string());
let _started = lock_server.start()?; let _started = lock_server.start()?;
@ -322,7 +323,7 @@ pub fn fix_maybe_exec_rustc(config: &Config) -> CargoResult<bool> {
let workspace_rustc = std::env::var("RUSTC_WORKSPACE_WRAPPER") let workspace_rustc = std::env::var("RUSTC_WORKSPACE_WRAPPER")
.map(PathBuf::from) .map(PathBuf::from)
.ok(); .ok();
let rustc = util::process(&args.rustc).wrapped(workspace_rustc.as_ref()); let rustc = ProcessBuilder::new(&args.rustc).wrapped(workspace_rustc.as_ref());
trace!("start rustfixing {:?}", args.file); trace!("start rustfixing {:?}", args.file);
let fixes = rustfix_crate(&lock_addr, &rustc, &args.file, &args, config)?; let fixes = rustfix_crate(&lock_addr, &rustc, &args.file, &args, config)?;

View File

@ -1,9 +1,10 @@
//! Registry authentication support. //! Registry authentication support.
use crate::sources::CRATES_IO_REGISTRY; use crate::sources::CRATES_IO_REGISTRY;
use crate::util::{config, process_error, CargoResult, CargoResultExt, Config}; use crate::util::{config, CargoResult, CargoResultExt, Config};
use anyhow::bail; use anyhow::bail;
use anyhow::format_err; use anyhow::format_err;
use cargo_util::ProcessError;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
@ -197,7 +198,7 @@ fn run_command(
Action::Store(_) => "failed to store token to registry", Action::Store(_) => "failed to store token to registry",
Action::Erase => "failed to erase token from registry", Action::Erase => "failed to erase token from registry",
}; };
return Err(process_error( return Err(ProcessError::new(
&format!( &format!(
"registry credential process `{}` {} `{}`", "registry credential process `{}` {} `{}`",
exe.display(), exe.display(),

View File

@ -4,9 +4,9 @@
use crate::core::GitReference; use crate::core::GitReference;
use crate::util::errors::{CargoResult, CargoResultExt}; use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::paths; use crate::util::paths;
use crate::util::process_builder::process;
use crate::util::{network, Config, IntoUrl, Progress}; use crate::util::{network, Config, IntoUrl, Progress};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use cargo_util::ProcessBuilder;
use curl::easy::List; use curl::easy::List;
use git2::{self, ErrorClass, ObjectType}; use git2::{self, ErrorClass, ObjectType};
use log::{debug, info}; use log::{debug, info};
@ -835,7 +835,7 @@ fn fetch_with_cli(
tags: bool, tags: bool,
config: &Config, config: &Config,
) -> CargoResult<()> { ) -> CargoResult<()> {
let mut cmd = process("git"); let mut cmd = ProcessBuilder::new("git");
cmd.arg("fetch"); cmd.arg("fetch");
if tags { if tags {
cmd.arg("--tags"); cmd.arg("--tags");

View File

@ -10,12 +10,13 @@ use std::sync::Arc;
use std::thread::{self, JoinHandle}; use std::thread::{self, JoinHandle};
use anyhow::{Context, Error}; use anyhow::{Context, Error};
use cargo_util::ProcessBuilder;
use log::warn; use log::warn;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::core::Edition; use crate::core::Edition;
use crate::util::errors::CargoResult; use crate::util::errors::CargoResult;
use crate::util::{Config, ProcessBuilder}; use crate::util::Config;
const DIAGNOSICS_SERVER_VAR: &str = "__CARGO_FIX_DIAGNOSTICS_SERVER"; const DIAGNOSICS_SERVER_VAR: &str = "__CARGO_FIX_DIAGNOSTICS_SERVER";
const PLEASE_REPORT_THIS_BUG: &str = const PLEASE_REPORT_THIS_BUG: &str =

View File

@ -3,10 +3,9 @@
use crate::core::{TargetKind, Workspace}; use crate::core::{TargetKind, Workspace};
use crate::ops::CompileOptions; use crate::ops::CompileOptions;
use anyhow::Error; use anyhow::Error;
use cargo_util::ProcessError;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{ExitStatus, Output};
use std::str;
pub type CargoResult<T> = anyhow::Result<T>; pub type CargoResult<T> = anyhow::Result<T>;
@ -186,41 +185,6 @@ impl<'a> Iterator for ManifestCauses<'a> {
impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {} impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
// =============================================================================
// Process errors
#[derive(Debug)]
pub struct ProcessError {
/// A detailed description to show to the user why the process failed.
pub desc: String,
/// The exit status of the process.
///
/// This can be `None` if the process failed to launch (like process not
/// found) or if the exit status wasn't a code but was instead something
/// like termination via a signal.
pub code: Option<i32>,
/// The stdout from the process.
///
/// This can be `None` if the process failed to launch, or the output was
/// not captured.
pub stdout: Option<Vec<u8>>,
/// The stderr from the process.
///
/// This can be `None` if the process failed to launch, or the output was
/// not captured.
pub stderr: Option<Vec<u8>>,
}
impl fmt::Display for ProcessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.desc.fmt(f)
}
}
impl std::error::Error for ProcessError {}
// ============================================================================= // =============================================================================
// Cargo test errors. // Cargo test errors.
@ -360,154 +324,6 @@ impl From<clap::Error> for CliError {
// ============================================================================= // =============================================================================
// Construction helpers // Construction helpers
/// Creates a new process error.
///
/// `status` can be `None` if the process did not launch.
/// `output` can be `None` if the process did not launch, or output was not captured.
pub fn process_error(
msg: &str,
status: Option<ExitStatus>,
output: Option<&Output>,
) -> ProcessError {
let exit = match status {
Some(s) => exit_status_to_string(s),
None => "never executed".to_string(),
};
process_error_raw(
msg,
status.and_then(|s| s.code()),
&exit,
output.map(|s| s.stdout.as_slice()),
output.map(|s| s.stderr.as_slice()),
)
}
pub fn process_error_raw(
msg: &str,
code: Option<i32>,
status: &str,
stdout: Option<&[u8]>,
stderr: Option<&[u8]>,
) -> ProcessError {
let mut desc = format!("{} ({})", msg, status);
if let Some(out) = stdout {
match str::from_utf8(out) {
Ok(s) if !s.trim().is_empty() => {
desc.push_str("\n--- stdout\n");
desc.push_str(s);
}
Ok(..) | Err(..) => {}
}
}
if let Some(out) = stderr {
match str::from_utf8(out) {
Ok(s) if !s.trim().is_empty() => {
desc.push_str("\n--- stderr\n");
desc.push_str(s);
}
Ok(..) | Err(..) => {}
}
}
ProcessError {
desc,
code,
stdout: stdout.map(|s| s.to_vec()),
stderr: stderr.map(|s| s.to_vec()),
}
}
pub fn exit_status_to_string(status: ExitStatus) -> String {
return status_to_string(status);
#[cfg(unix)]
fn status_to_string(status: ExitStatus) -> String {
use std::os::unix::process::*;
if let Some(signal) = status.signal() {
let name = match signal as libc::c_int {
libc::SIGABRT => ", SIGABRT: process abort signal",
libc::SIGALRM => ", SIGALRM: alarm clock",
libc::SIGFPE => ", SIGFPE: erroneous arithmetic operation",
libc::SIGHUP => ", SIGHUP: hangup",
libc::SIGILL => ", SIGILL: illegal instruction",
libc::SIGINT => ", SIGINT: terminal interrupt signal",
libc::SIGKILL => ", SIGKILL: kill",
libc::SIGPIPE => ", SIGPIPE: write on a pipe with no one to read",
libc::SIGQUIT => ", SIGQUIT: terminal quit signal",
libc::SIGSEGV => ", SIGSEGV: invalid memory reference",
libc::SIGTERM => ", SIGTERM: termination signal",
libc::SIGBUS => ", SIGBUS: access to undefined memory",
#[cfg(not(target_os = "haiku"))]
libc::SIGSYS => ", SIGSYS: bad system call",
libc::SIGTRAP => ", SIGTRAP: trace/breakpoint trap",
_ => "",
};
format!("signal: {}{}", signal, name)
} else {
status.to_string()
}
}
#[cfg(windows)]
fn status_to_string(status: ExitStatus) -> String {
use winapi::shared::minwindef::DWORD;
use winapi::um::winnt::*;
let mut base = status.to_string();
let extra = match status.code().unwrap() as DWORD {
STATUS_ACCESS_VIOLATION => "STATUS_ACCESS_VIOLATION",
STATUS_IN_PAGE_ERROR => "STATUS_IN_PAGE_ERROR",
STATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
STATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
STATUS_NO_MEMORY => "STATUS_NO_MEMORY",
STATUS_ILLEGAL_INSTRUCTION => "STATUS_ILLEGAL_INSTRUCTION",
STATUS_NONCONTINUABLE_EXCEPTION => "STATUS_NONCONTINUABLE_EXCEPTION",
STATUS_INVALID_DISPOSITION => "STATUS_INVALID_DISPOSITION",
STATUS_ARRAY_BOUNDS_EXCEEDED => "STATUS_ARRAY_BOUNDS_EXCEEDED",
STATUS_FLOAT_DENORMAL_OPERAND => "STATUS_FLOAT_DENORMAL_OPERAND",
STATUS_FLOAT_DIVIDE_BY_ZERO => "STATUS_FLOAT_DIVIDE_BY_ZERO",
STATUS_FLOAT_INEXACT_RESULT => "STATUS_FLOAT_INEXACT_RESULT",
STATUS_FLOAT_INVALID_OPERATION => "STATUS_FLOAT_INVALID_OPERATION",
STATUS_FLOAT_OVERFLOW => "STATUS_FLOAT_OVERFLOW",
STATUS_FLOAT_STACK_CHECK => "STATUS_FLOAT_STACK_CHECK",
STATUS_FLOAT_UNDERFLOW => "STATUS_FLOAT_UNDERFLOW",
STATUS_INTEGER_DIVIDE_BY_ZERO => "STATUS_INTEGER_DIVIDE_BY_ZERO",
STATUS_INTEGER_OVERFLOW => "STATUS_INTEGER_OVERFLOW",
STATUS_PRIVILEGED_INSTRUCTION => "STATUS_PRIVILEGED_INSTRUCTION",
STATUS_STACK_OVERFLOW => "STATUS_STACK_OVERFLOW",
STATUS_DLL_NOT_FOUND => "STATUS_DLL_NOT_FOUND",
STATUS_ORDINAL_NOT_FOUND => "STATUS_ORDINAL_NOT_FOUND",
STATUS_ENTRYPOINT_NOT_FOUND => "STATUS_ENTRYPOINT_NOT_FOUND",
STATUS_CONTROL_C_EXIT => "STATUS_CONTROL_C_EXIT",
STATUS_DLL_INIT_FAILED => "STATUS_DLL_INIT_FAILED",
STATUS_FLOAT_MULTIPLE_FAULTS => "STATUS_FLOAT_MULTIPLE_FAULTS",
STATUS_FLOAT_MULTIPLE_TRAPS => "STATUS_FLOAT_MULTIPLE_TRAPS",
STATUS_REG_NAT_CONSUMPTION => "STATUS_REG_NAT_CONSUMPTION",
STATUS_HEAP_CORRUPTION => "STATUS_HEAP_CORRUPTION",
STATUS_STACK_BUFFER_OVERRUN => "STATUS_STACK_BUFFER_OVERRUN",
STATUS_ASSERTION_FAILURE => "STATUS_ASSERTION_FAILURE",
_ => return base,
};
base.push_str(", ");
base.push_str(extra);
base
}
}
pub fn is_simple_exit_code(code: i32) -> bool {
// Typical unix exit codes are 0 to 127.
// Windows doesn't have anything "typical", and is a
// 32-bit number (which appears signed here, but is really
// unsigned). However, most of the interesting NTSTATUS
// codes are very large. This is just a rough
// approximation of which codes are "normal" and which
// ones are abnormal termination.
code >= 0 && code <= 127
}
pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error { pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
InternalError::new(anyhow::format_err!("{}", error)).into() InternalError::new(anyhow::format_err!("{}", error)).into()
} }

View File

@ -4,9 +4,8 @@ pub use self::canonical_url::CanonicalUrl;
pub use self::config::{homedir, Config, ConfigValue}; pub use self::config::{homedir, Config, ConfigValue};
pub use self::dependency_queue::DependencyQueue; pub use self::dependency_queue::DependencyQueue;
pub use self::diagnostic_server::RustfixDiagnosticServer; pub use self::diagnostic_server::RustfixDiagnosticServer;
pub use self::errors::{exit_status_to_string, internal, process_error, process_error_raw}; pub use self::errors::{internal, CargoResult, CargoResultExt, CliResult, Test};
pub use self::errors::{CargoResult, CargoResultExt, CliResult, Test}; pub use self::errors::{CargoTestError, CliError};
pub use self::errors::{CargoTestError, CliError, ProcessError};
pub use self::flock::{FileLock, Filesystem}; pub use self::flock::{FileLock, Filesystem};
pub use self::graph::Graph; pub use self::graph::Graph;
pub use self::hasher::StableHasher; pub use self::hasher::StableHasher;
@ -17,10 +16,8 @@ pub use self::lev_distance::{closest, closest_msg, lev_distance};
pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted}; pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted};
pub use self::paths::{bytes2path, dylib_path, join_paths, path2bytes}; pub use self::paths::{bytes2path, dylib_path, join_paths, path2bytes};
pub use self::paths::{dylib_path_envvar, normalize_path}; pub use self::paths::{dylib_path_envvar, normalize_path};
pub use self::process_builder::{process, ProcessBuilder};
pub use self::progress::{Progress, ProgressStyle}; pub use self::progress::{Progress, ProgressStyle};
pub use self::queue::Queue; pub use self::queue::Queue;
pub use self::read2::read2;
pub use self::restricted_names::validate_package_name; pub use self::restricted_names::validate_package_name;
pub use self::rustc::Rustc; pub use self::rustc::Rustc;
pub use self::sha256::Sha256; pub use self::sha256::Sha256;
@ -52,11 +49,9 @@ mod lockserver;
pub mod machine_message; pub mod machine_message;
pub mod network; pub mod network;
pub mod paths; pub mod paths;
pub mod process_builder;
pub mod profile; pub mod profile;
mod progress; mod progress;
mod queue; mod queue;
mod read2;
pub mod restricted_names; pub mod restricted_names;
pub mod rustc; pub mod rustc;
mod sha256; mod sha256;

View File

@ -4,12 +4,13 @@ use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Mutex; use std::sync::Mutex;
use cargo_util::{ProcessBuilder, ProcessError};
use log::{debug, info, warn}; use log::{debug, info, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::util::interning::InternedString; use crate::util::interning::InternedString;
use crate::util::paths; use crate::util::paths;
use crate::util::{self, profile, CargoResult, CargoResultExt, ProcessBuilder, StableHasher}; use crate::util::{profile, CargoResult, CargoResultExt, StableHasher};
/// Information on the `rustc` executable /// Information on the `rustc` executable
#[derive(Debug)] #[derive(Debug)]
@ -47,7 +48,7 @@ impl Rustc {
let mut cache = Cache::load(&path, rustup_rustc, cache_location); let mut cache = Cache::load(&path, rustup_rustc, cache_location);
let mut cmd = util::process(&path); let mut cmd = ProcessBuilder::new(&path);
cmd.arg("-vV"); cmd.arg("-vV");
let verbose_version = cache.cached_output(&cmd, 0)?.0; let verbose_version = cache.cached_output(&cmd, 0)?.0;
@ -86,18 +87,18 @@ impl Rustc {
/// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`. /// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`.
pub fn process(&self) -> ProcessBuilder { pub fn process(&self) -> ProcessBuilder {
util::process(self.path.as_path()).wrapped(self.wrapper.as_ref()) ProcessBuilder::new(self.path.as_path()).wrapped(self.wrapper.as_ref())
} }
/// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`. /// Gets a process builder set up to use the found rustc version, with a wrapper if `Some`.
pub fn workspace_process(&self) -> ProcessBuilder { pub fn workspace_process(&self) -> ProcessBuilder {
util::process(self.path.as_path()) ProcessBuilder::new(self.path.as_path())
.wrapped(self.workspace_wrapper.as_ref()) .wrapped(self.workspace_wrapper.as_ref())
.wrapped(self.wrapper.as_ref()) .wrapped(self.wrapper.as_ref())
} }
pub fn process_no_wrapper(&self) -> ProcessBuilder { pub fn process_no_wrapper(&self) -> ProcessBuilder {
util::process(&self.path) ProcessBuilder::new(&self.path)
} }
/// Gets the output for the given command. /// Gets the output for the given command.
@ -232,7 +233,7 @@ impl Cache {
status: if output.status.success() { status: if output.status.success() {
String::new() String::new()
} else { } else {
util::exit_status_to_string(output.status) cargo_util::exit_status_to_string(output.status)
}, },
code: output.status.code(), code: output.status.code(),
stdout, stdout,
@ -245,7 +246,7 @@ impl Cache {
if output.success { if output.success {
Ok((output.stdout.clone(), output.stderr.clone())) Ok((output.stdout.clone(), output.stderr.clone()))
} else { } else {
Err(util::process_error_raw( Err(ProcessError::new_raw(
&format!("process didn't exit successfully: {}", cmd), &format!("process didn't exit successfully: {}", cmd),
output.code, output.code,
&output.status, &output.status,

View File

@ -1,5 +1,6 @@
use crate::util::paths; use crate::util::paths;
use crate::util::{process, CargoResult}; use crate::util::CargoResult;
use cargo_util::ProcessBuilder;
use std::path::Path; use std::path::Path;
// Check if we are in an existing repo. We define that to be true if either: // Check if we are in an existing repo. We define that to be true if either:
@ -41,11 +42,15 @@ impl GitRepo {
impl HgRepo { impl HgRepo {
pub fn init(path: &Path, cwd: &Path) -> CargoResult<HgRepo> { pub fn init(path: &Path, cwd: &Path) -> CargoResult<HgRepo> {
process("hg").cwd(cwd).arg("init").arg(path).exec()?; ProcessBuilder::new("hg")
.cwd(cwd)
.arg("init")
.arg(path)
.exec()?;
Ok(HgRepo) Ok(HgRepo)
} }
pub fn discover(path: &Path, cwd: &Path) -> CargoResult<HgRepo> { pub fn discover(path: &Path, cwd: &Path) -> CargoResult<HgRepo> {
process("hg") ProcessBuilder::new("hg")
.cwd(cwd) .cwd(cwd)
.arg("--cwd") .arg("--cwd")
.arg(path) .arg(path)
@ -57,7 +62,11 @@ impl HgRepo {
impl PijulRepo { impl PijulRepo {
pub fn init(path: &Path, cwd: &Path) -> CargoResult<PijulRepo> { pub fn init(path: &Path, cwd: &Path) -> CargoResult<PijulRepo> {
process("pijul").cwd(cwd).arg("init").arg(path).exec()?; ProcessBuilder::new("pijul")
.cwd(cwd)
.arg("init")
.arg(path)
.exec()?;
Ok(PijulRepo) Ok(PijulRepo)
} }
} }
@ -73,28 +82,28 @@ impl FossilRepo {
db_path.push(db_fname); db_path.push(db_fname);
// then create the fossil DB in that location // then create the fossil DB in that location
process("fossil") ProcessBuilder::new("fossil")
.cwd(cwd) .cwd(cwd)
.arg("init") .arg("init")
.arg(&db_path) .arg(&db_path)
.exec()?; .exec()?;
// open it in that new directory // open it in that new directory
process("fossil") ProcessBuilder::new("fossil")
.cwd(&path) .cwd(&path)
.arg("open") .arg("open")
.arg(db_fname) .arg(db_fname)
.exec()?; .exec()?;
// set `target` as ignoreable and cleanable // set `target` as ignoreable and cleanable
process("fossil") ProcessBuilder::new("fossil")
.cwd(cwd) .cwd(cwd)
.arg("settings") .arg("settings")
.arg("ignore-glob") .arg("ignore-glob")
.arg("target") .arg("target")
.exec()?; .exec()?;
process("fossil") ProcessBuilder::new("fossil")
.cwd(cwd) .cwd(cwd)
.arg("settings") .arg("settings")
.arg("clean-glob") .arg("clean-glob")

View File

@ -1,10 +1,10 @@
use super::ProcessBuilder;
use crate::core::compiler::Unit; use crate::core::compiler::Unit;
use crate::core::manifest::TargetSourcePath; use crate::core::manifest::TargetSourcePath;
use crate::core::{Target, Workspace}; use crate::core::{Target, Workspace};
use crate::ops::CompileOptions; use crate::ops::CompileOptions;
use crate::util::CargoResult; use crate::util::CargoResult;
use anyhow::bail; use anyhow::bail;
use cargo_util::ProcessBuilder;
use std::fmt::Write; use std::fmt::Write;
use std::path::PathBuf; use std::path::PathBuf;

View File

@ -10,11 +10,11 @@
//! cargo test --test testsuite -- old_cargos --nocapture --ignored //! cargo test --test testsuite -- old_cargos --nocapture --ignored
//! ``` //! ```
use cargo::util::{ProcessBuilder, ProcessError};
use cargo::CargoResult; use cargo::CargoResult;
use cargo_test_support::paths::CargoPathExt; use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::{self, Dependency, Package}; use cargo_test_support::registry::{self, Dependency, Package};
use cargo_test_support::{cargo_exe, execs, paths, process, project, rustc_host}; use cargo_test_support::{cargo_exe, execs, paths, process, project, rustc_host};
use cargo_util::{ProcessBuilder, ProcessError};
use semver::Version; use semver::Version;
use std::fs; use std::fs;
@ -68,7 +68,7 @@ fn collect_all_toolchains() -> Vec<(Version, String)> {
format!("nightly-{}", host), format!("nightly-{}", host),
]; ];
let output = cargo::util::process("rustup") let output = ProcessBuilder::new("rustup")
.args(&["toolchain", "list"]) .args(&["toolchain", "list"])
.exec_with_output() .exec_with_output()
.expect("rustup should be installed"); .expect("rustup should be installed");