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' -p cargo-test-support
- run: cargo test -p cargo-platform
- run: cargo test -p cargo-util
- 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-gnome-secret/Cargo.toml

View File

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

View File

@ -11,6 +11,7 @@ doctest = false
[dependencies]
cargo = { path = "../.." }
cargo-test-macro = { path = "../cargo-test-macro" }
cargo-util = { path = "../cargo-util" }
filetime = "0.2"
flate2 = { version = "1.0", default-features = false, features = ["zlib"] }
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.
use crate::{basic_manifest, main_file, project};
use cargo::util::ProcessError;
use cargo::CargoResult;
use cargo_util::ProcessError;
use std::env;
use std::fmt::Write;
use std::process::{Command, Output};

View File

@ -15,7 +15,8 @@ use std::process::{Command, Output};
use std::str;
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 url::Url;
@ -1569,12 +1570,12 @@ pub fn is_nightly() -> bool {
.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())
}
fn _process(t: &OsStr) -> cargo::util::ProcessBuilder {
let mut p = cargo::util::process(t);
fn _process(t: &OsStr) -> ProcessBuilder {
let mut p = ProcessBuilder::new(t);
// In general just clear out all cargo-specific configuration already in the
// 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;
}
impl ChannelChanger for cargo::util::ProcessBuilder {
impl ChannelChanger for ProcessBuilder {
fn masquerade_as_nightly_cargo(&mut self) -> &mut Self {
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."
[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.
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 anyhow::bail;
use crate::process_error::ProcessError;
use crate::read2;
use anyhow::{bail, Context, Result};
use jobserver::Client;
use shell_escape::escape;
use std::collections::BTreeMap;
@ -10,7 +11,7 @@ use std::iter::once;
use std::path::Path;
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)]
pub struct ProcessBuilder {
/// The program to execute.
@ -21,10 +22,10 @@ pub struct ProcessBuilder {
env: BTreeMap<String, Option<OsString>>,
/// The directory to run the program from.
cwd: Option<OsString>,
/// The `make` jobserver. See the [jobserver crate][jobserver_docs] for
/// The `make` jobserver. See the [jobserver crate] for
/// more information.
///
/// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
/// [jobserver crate]: https://docs.rs/jobserver/
jobserver: Option<Client>,
/// `true` to include environment variable in display.
display_env_vars: bool,
@ -58,6 +59,18 @@ impl fmt::Display for 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.
pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder {
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.
pub fn exec(&self) -> CargoResult<()> {
pub fn exec(&self) -> Result<()> {
let mut command = self.build_command();
let exit = command.status().chain_err(|| {
process_error(&format!("could not execute process {}", self), None, None)
let exit = command.status().with_context(|| {
ProcessError::new(&format!("could not execute process {}", self), None, None)
})?;
if exit.success() {
Ok(())
} else {
Err(process_error(
Err(ProcessError::new(
&format!("process didn't exit successfully: {}", self),
Some(exit),
None,
@ -182,22 +195,22 @@ impl ProcessBuilder {
/// 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
/// (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)
}
/// 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 output = command.output().chain_err(|| {
process_error(&format!("could not execute process {}", self), None, None)
let output = command.output().with_context(|| {
ProcessError::new(&format!("could not execute process {}", self), None, None)
})?;
if output.status.success() {
Ok(output)
} else {
Err(process_error(
Err(ProcessError::new(
&format!("process didn't exit successfully: {}", self),
Some(output.status),
Some(&output),
@ -217,10 +230,10 @@ impl ProcessBuilder {
/// output.
pub fn exec_with_streaming(
&self,
on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
on_stdout_line: &mut dyn FnMut(&str) -> Result<()>,
on_stderr_line: &mut dyn FnMut(&str) -> Result<()>,
capture_output: bool,
) -> CargoResult<Output> {
) -> Result<Output> {
let mut stdout = Vec::new();
let mut stderr = Vec::new();
@ -274,7 +287,9 @@ impl ProcessBuilder {
})?;
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 {
status,
stdout,
@ -284,14 +299,14 @@ impl ProcessBuilder {
{
let to_print = if capture_output { Some(&output) } else { None };
if let Some(e) = callback_error {
let cx = process_error(
let cx = ProcessError::new(
&format!("failed to parse process output: {}", self),
Some(output.status),
to_print,
);
bail!(anyhow::Error::new(cx).context(e));
} else if !output.status.success() {
bail!(process_error(
bail!(ProcessError::new(
&format!("process didn't exit successfully: {}", self),
Some(output.status),
to_print,
@ -333,9 +348,9 @@ impl ProcessBuilder {
/// # Examples
///
/// ```rust
/// use cargo::util::{ProcessBuilder, process};
/// use cargo_util::ProcessBuilder;
/// // Running this would execute `rustc`
/// let cmd: ProcessBuilder = process("rustc");
/// let cmd = ProcessBuilder::new("rustc");
///
/// // Running this will execute `sccache rustc`
/// 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)]
mod imp {
use crate::util::{process_error, ProcessBuilder};
use crate::CargoResult;
use super::{ProcessBuilder, ProcessError};
use anyhow::Result;
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 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),
None,
None,
@ -391,8 +394,8 @@ mod imp {
#[cfg(windows)]
mod imp {
use crate::util::{process_error, ProcessBuilder};
use crate::CargoResult;
use super::{ProcessBuilder, ProcessError};
use anyhow::Result;
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
use winapi::um::consoleapi::SetConsoleCtrlHandler;
@ -401,10 +404,10 @@ mod imp {
TRUE
}
pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
pub fn exec_replace(process_builder: &ProcessBuilder) -> Result<()> {
unsafe {
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 = [
'crates/cargo-platform',
'crates/cargo-util',
'crates/crates-io',
'.',
]

View File

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

View File

@ -1,7 +1,6 @@
use crate::command_prelude::*;
use anyhow::Error;
use cargo::ops::{self, CompileFilter, FilterRule, LibRule};
use cargo::util::errors;
pub fn cli() -> App {
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 e = match err.code {
// 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),
None => CliError::new(Error::from(err).context(context), 101),
};

View File

@ -3,15 +3,15 @@
#![warn(clippy::needless_borrow)]
#![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::env;
use std::fs;
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 commands;
@ -159,7 +159,7 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli
};
let cargo_exe = config.cargo_exe()?;
let err = match util::process(&command)
let err = match ProcessBuilder::new(&command)
.env(cargo::CARGO_ENV, cargo_exe)
.args(args)
.exec_replace()

View File

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

View File

@ -3,8 +3,9 @@ use crate::core::compiler::{
};
use crate::core::{Dependency, Target, TargetKind, Workspace};
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_util::ProcessBuilder;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};

View File

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

View File

@ -4,12 +4,13 @@ use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use cargo_platform::CfgExpr;
use cargo_util::ProcessBuilder;
use semver::Version;
use super::BuildContext;
use crate::core::compiler::{CompileKind, Metadata, Unit};
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`.
pub struct Doctest {
@ -184,7 +185,7 @@ impl<'cfg> Compilation<'cfg> {
unit: &Unit,
script_meta: Option<Metadata>,
) -> CargoResult<ProcessBuilder> {
let rustdoc = process(&*self.config.rustdoc()?);
let rustdoc = ProcessBuilder::new(&*self.config.rustdoc()?);
let cmd = fill_rustc_tool_env(rustdoc, unit);
let mut p = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, true)?;
unit.target.edition().cmd_edition_arg(&mut p);
@ -207,7 +208,13 @@ impl<'cfg> Compilation<'cfg> {
cmd: T,
pkg: &Package,
) -> 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>)> {
@ -229,12 +236,12 @@ impl<'cfg> Compilation<'cfg> {
script_meta: Option<Metadata>,
) -> CargoResult<ProcessBuilder> {
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.arg(cmd);
builder
} else {
process(cmd)
ProcessBuilder::new(cmd)
};
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 anyhow::{bail, format_err};
use cargo_util::ProcessBuilder;
use filetime::FileTime;
use log::{debug, info};
use serde::de;
@ -334,7 +335,7 @@ use crate::util;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::interning::InternedString;
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::job::{Job, Work};

View File

@ -57,6 +57,7 @@ use std::sync::Arc;
use std::time::Duration;
use anyhow::format_err;
use cargo_util::ProcessBuilder;
use crossbeam_utils::thread::Scope;
use jobserver::{Acquired, Client, HelperThread};
use log::{debug, info, trace};
@ -78,7 +79,7 @@ use crate::drop_eprint;
use crate::util::diagnostic_server::{self, DiagnosticPrinter};
use crate::util::machine_message::{self, Message as _};
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};
/// 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::profiles::{PanicStrategy, Profile, Strip};
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::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 cargo_util::{ProcessBuilder, ProcessError};
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()
.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,
}
}

View File

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

View File

@ -97,10 +97,11 @@ use std::fmt;
use std::str::FromStr;
use anyhow::{bail, Error};
use cargo_util::ProcessBuilder;
use serde::{Deserialize, Serialize};
use crate::util::errors::CargoResult;
use crate::util::{indented_lines, ProcessBuilder};
use crate::util::indented_lines;
use crate::Config;
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::shell::Verbosity;
use crate::core::{TargetKind, Workspace};
use crate::ops;
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 compile_opts: ops::CompileOptions,

View File

@ -46,6 +46,7 @@ use std::process::{self, Command, ExitStatus};
use std::str;
use anyhow::{bail, Context, Error};
use cargo_util::ProcessBuilder;
use log::{debug, trace, warn};
use rustfix::diagnostics::Diagnostic;
use rustfix::{self, CodeFix};
@ -57,7 +58,7 @@ use crate::core::{Edition, MaybePackage, Workspace};
use crate::ops::{self, CompileOptions};
use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer};
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::{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.
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());
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")
.map(PathBuf::from)
.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);
let fixes = rustfix_crate(&lock_addr, &rustc, &args.file, &args, config)?;

View File

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

View File

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

View File

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

View File

@ -3,10 +3,9 @@
use crate::core::{TargetKind, Workspace};
use crate::ops::CompileOptions;
use anyhow::Error;
use cargo_util::ProcessError;
use std::fmt;
use std::path::PathBuf;
use std::process::{ExitStatus, Output};
use std::str;
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> {}
// =============================================================================
// 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.
@ -360,154 +324,6 @@ impl From<clap::Error> for CliError {
// =============================================================================
// 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 {
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::dependency_queue::DependencyQueue;
pub use self::diagnostic_server::RustfixDiagnosticServer;
pub use self::errors::{exit_status_to_string, internal, process_error, process_error_raw};
pub use self::errors::{CargoResult, CargoResultExt, CliResult, Test};
pub use self::errors::{CargoTestError, CliError, ProcessError};
pub use self::errors::{internal, CargoResult, CargoResultExt, CliResult, Test};
pub use self::errors::{CargoTestError, CliError};
pub use self::flock::{FileLock, Filesystem};
pub use self::graph::Graph;
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::paths::{bytes2path, dylib_path, join_paths, path2bytes};
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::queue::Queue;
pub use self::read2::read2;
pub use self::restricted_names::validate_package_name;
pub use self::rustc::Rustc;
pub use self::sha256::Sha256;
@ -52,11 +49,9 @@ mod lockserver;
pub mod machine_message;
pub mod network;
pub mod paths;
pub mod process_builder;
pub mod profile;
mod progress;
mod queue;
mod read2;
pub mod restricted_names;
pub mod rustc;
mod sha256;

View File

@ -4,12 +4,13 @@ use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use cargo_util::{ProcessBuilder, ProcessError};
use log::{debug, info, warn};
use serde::{Deserialize, Serialize};
use crate::util::interning::InternedString;
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
#[derive(Debug)]
@ -47,7 +48,7 @@ impl Rustc {
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");
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`.
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`.
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.wrapper.as_ref())
}
pub fn process_no_wrapper(&self) -> ProcessBuilder {
util::process(&self.path)
ProcessBuilder::new(&self.path)
}
/// Gets the output for the given command.
@ -232,7 +233,7 @@ impl Cache {
status: if output.status.success() {
String::new()
} else {
util::exit_status_to_string(output.status)
cargo_util::exit_status_to_string(output.status)
},
code: output.status.code(),
stdout,
@ -245,7 +246,7 @@ impl Cache {
if output.success {
Ok((output.stdout.clone(), output.stderr.clone()))
} else {
Err(util::process_error_raw(
Err(ProcessError::new_raw(
&format!("process didn't exit successfully: {}", cmd),
output.code,
&output.status,

View File

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

View File

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

View File

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