mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
Rollup merge of #82411 - ijackson:fix-exitstatus, r=dtolnay
Fixes to ExitStatus and its docs * On Unix, properly display every possible wait status (and don't panic on weird values) * In the documentation, be clear and consistent about "exit status" vs "wait status".
This commit is contained in:
commit
74e74e9df8
@ -885,7 +885,7 @@ impl Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a command as a child process, waiting for it to finish and
|
/// Executes a command as a child process, waiting for it to finish and
|
||||||
/// collecting its exit status.
|
/// collecting its status.
|
||||||
///
|
///
|
||||||
/// By default, stdin, stdout and stderr are inherited from the parent.
|
/// By default, stdin, stdout and stderr are inherited from the parent.
|
||||||
///
|
///
|
||||||
@ -899,7 +899,7 @@ impl Command {
|
|||||||
/// .status()
|
/// .status()
|
||||||
/// .expect("failed to execute process");
|
/// .expect("failed to execute process");
|
||||||
///
|
///
|
||||||
/// println!("process exited with: {}", status);
|
/// println!("process finished with: {}", status);
|
||||||
///
|
///
|
||||||
/// assert!(status.success());
|
/// assert!(status.success());
|
||||||
/// ```
|
/// ```
|
||||||
@ -1368,11 +1368,17 @@ impl From<fs::File> for Stdio {
|
|||||||
|
|
||||||
/// Describes the result of a process after it has terminated.
|
/// Describes the result of a process after it has terminated.
|
||||||
///
|
///
|
||||||
/// This `struct` is used to represent the exit status of a child process.
|
/// This `struct` is used to represent the exit status or other termination of a child process.
|
||||||
/// Child processes are created via the [`Command`] struct and their exit
|
/// Child processes are created via the [`Command`] struct and their exit
|
||||||
/// status is exposed through the [`status`] method, or the [`wait`] method
|
/// status is exposed through the [`status`] method, or the [`wait`] method
|
||||||
/// of a [`Child`] process.
|
/// of a [`Child`] process.
|
||||||
///
|
///
|
||||||
|
/// An `ExitStatus` represents every possible disposition of a process. On Unix this
|
||||||
|
/// is the **wait status**. It is *not* simply an *exit status* (a value passed to `exit`).
|
||||||
|
///
|
||||||
|
/// For proper error reporting of failed processes, print the value of `ExitStatus` using its
|
||||||
|
/// implementation of [`Display`](crate::fmt::Display).
|
||||||
|
///
|
||||||
/// [`status`]: Command::status
|
/// [`status`]: Command::status
|
||||||
/// [`wait`]: Child::wait
|
/// [`wait`]: Child::wait
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
@ -1400,7 +1406,7 @@ impl ExitStatus {
|
|||||||
/// if status.success() {
|
/// if status.success() {
|
||||||
/// println!("'projects/' directory created");
|
/// println!("'projects/' directory created");
|
||||||
/// } else {
|
/// } else {
|
||||||
/// println!("failed to create 'projects/' directory");
|
/// println!("failed to create 'projects/' directory: {}", status);
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "process", since = "1.0.0")]
|
#[stable(feature = "process", since = "1.0.0")]
|
||||||
@ -1410,9 +1416,14 @@ impl ExitStatus {
|
|||||||
|
|
||||||
/// Returns the exit code of the process, if any.
|
/// Returns the exit code of the process, if any.
|
||||||
///
|
///
|
||||||
/// On Unix, this will return `None` if the process was terminated
|
/// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the
|
||||||
/// by a signal; `std::os::unix` provides an extension trait for
|
/// process finished by calling `exit`. Note that on Unix the exit status is truncated to 8
|
||||||
/// extracting the signal and other details from the `ExitStatus`.
|
/// bits, and that values that didn't come from a program's call to `exit` may be invented the
|
||||||
|
/// runtime system (often, for example, 255, 254, 127 or 126).
|
||||||
|
///
|
||||||
|
/// On Unix, this will return `None` if the process was terminated by a signal.
|
||||||
|
/// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) is an
|
||||||
|
/// extension trait for extracting any such signal, and other details, from the `ExitStatus`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -188,12 +188,20 @@ impl CommandExt for process::Command {
|
|||||||
|
|
||||||
/// Unix-specific extensions to [`process::ExitStatus`].
|
/// Unix-specific extensions to [`process::ExitStatus`].
|
||||||
///
|
///
|
||||||
|
/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as passed to the
|
||||||
|
/// `exit` system call or returned by [`ExitStatus::code()`](crate::process::ExitStatus::code).
|
||||||
|
/// It represents **any wait status**, as returned by one of the `wait` family of system calls.
|
||||||
|
///
|
||||||
|
/// This is because a Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but
|
||||||
|
/// can also represent other kinds of process event.
|
||||||
|
///
|
||||||
/// This trait is sealed: it cannot be implemented outside the standard library.
|
/// This trait is sealed: it cannot be implemented outside the standard library.
|
||||||
/// This is so that future additional methods are not breaking changes.
|
/// This is so that future additional methods are not breaking changes.
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub trait ExitStatusExt: Sealed {
|
pub trait ExitStatusExt: Sealed {
|
||||||
/// Creates a new `ExitStatus` from the raw underlying `i32` return value of
|
/// Creates a new `ExitStatus` from the raw underlying integer status value from `wait`
|
||||||
/// a process.
|
///
|
||||||
|
/// The value should be a **wait status, not an exit status**.
|
||||||
#[stable(feature = "exit_status_from", since = "1.12.0")]
|
#[stable(feature = "exit_status_from", since = "1.12.0")]
|
||||||
fn from_raw(raw: i32) -> Self;
|
fn from_raw(raw: i32) -> Self;
|
||||||
|
|
||||||
@ -222,6 +230,8 @@ pub trait ExitStatusExt: Sealed {
|
|||||||
fn continued(&self) -> bool;
|
fn continued(&self) -> bool;
|
||||||
|
|
||||||
/// Returns the underlying raw `wait` status.
|
/// Returns the underlying raw `wait` status.
|
||||||
|
///
|
||||||
|
/// The returned integer is a **wait status, not an exit status**.
|
||||||
#[unstable(feature = "unix_process_wait_more", issue = "80695")]
|
#[unstable(feature = "unix_process_wait_more", issue = "80695")]
|
||||||
fn into_raw(self) -> i32;
|
fn into_raw(self) -> i32;
|
||||||
}
|
}
|
||||||
|
@ -527,9 +527,22 @@ impl fmt::Display for ExitStatus {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(code) = self.code() {
|
if let Some(code) = self.code() {
|
||||||
write!(f, "exit code: {}", code)
|
write!(f, "exit code: {}", code)
|
||||||
|
} else if let Some(signal) = self.signal() {
|
||||||
|
if self.core_dumped() {
|
||||||
|
write!(f, "signal: {} (core dumped)", signal)
|
||||||
|
} else {
|
||||||
|
write!(f, "signal: {}", signal)
|
||||||
|
}
|
||||||
|
} else if let Some(signal) = self.stopped_signal() {
|
||||||
|
write!(f, "stopped (not terminated) by signal: {}", signal)
|
||||||
|
} else if self.continued() {
|
||||||
|
write!(f, "continued (WIFCONTINUED)")
|
||||||
} else {
|
} else {
|
||||||
let signal = self.signal().unwrap();
|
write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
|
||||||
write!(f, "signal: {}", signal)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "process_unix/tests.rs"]
|
||||||
|
mod tests;
|
||||||
|
30
library/std/src/sys/unix/process/process_unix/tests.rs
Normal file
30
library/std/src/sys/unix/process/process_unix/tests.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#[test]
|
||||||
|
fn exitstatus_display_tests() {
|
||||||
|
// In practice this is the same on every Unix.
|
||||||
|
// If some weird platform turns out to be different, and this test fails, use #[cfg].
|
||||||
|
use crate::os::unix::process::ExitStatusExt;
|
||||||
|
use crate::process::ExitStatus;
|
||||||
|
|
||||||
|
let t = |v, s| assert_eq!(s, format!("{}", <ExitStatus as ExitStatusExt>::from_raw(v)));
|
||||||
|
|
||||||
|
t(0x0000f, "signal: 15");
|
||||||
|
t(0x0008b, "signal: 11 (core dumped)");
|
||||||
|
t(0x00000, "exit code: 0");
|
||||||
|
t(0x0ff00, "exit code: 255");
|
||||||
|
|
||||||
|
// On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
|
||||||
|
// https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956
|
||||||
|
// The purpose of this test is to test our string formatting, not our understanding of the wait
|
||||||
|
// status magic numbers. So restrict these to Linux.
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
t(0x0137f, "stopped (not terminated) by signal: 19");
|
||||||
|
t(0x0ffff, "continued (WIFCONTINUED)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testing "unrecognised wait status" is hard because the wait.h macros typically
|
||||||
|
// assume that the value came from wait and isn't mad. With the glibc I have here
|
||||||
|
// this works:
|
||||||
|
if cfg!(all(target_os = "linux", target_env = "gnu")) {
|
||||||
|
t(0x000ff, "unrecognised wait status: 255 0xff");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user