Auto merge of #97176 - kraktus:cmd_debug, r=the8472
More verbose `Debug` implementation of `std::process:Command`
Mainly based on commit: ccc019aabf
from https://github.com/zackmdavis
close https://github.com/rust-lang/rust/issues/42200
This commit is contained in:
commit
92c1937a90
@ -1038,6 +1038,15 @@ impl fmt::Debug for Command {
|
|||||||
/// Format the program and arguments of a Command for display. Any
|
/// Format the program and arguments of a Command for display. Any
|
||||||
/// non-utf8 data is lossily converted using the utf8 replacement
|
/// non-utf8 data is lossily converted using the utf8 replacement
|
||||||
/// character.
|
/// character.
|
||||||
|
///
|
||||||
|
/// The default format approximates a shell invocation of the program along with its
|
||||||
|
/// arguments. It does not include most of the other command properties. The output is not guaranteed to work
|
||||||
|
/// (e.g. due to lack of shell-escaping or differences in path resolution)
|
||||||
|
/// On some platforms you can use [the alternate syntax] to show more fields.
|
||||||
|
///
|
||||||
|
/// Note that the debug implementation is platform-specific.
|
||||||
|
///
|
||||||
|
/// [the alternate syntax]: fmt#sign0
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.inner.fmt(f)
|
self.inner.fmt(f)
|
||||||
}
|
}
|
||||||
|
@ -417,6 +417,100 @@ fn env_empty() {
|
|||||||
assert!(p.is_ok());
|
assert!(p.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
|
||||||
|
fn main() {
|
||||||
|
const PIDFD: &'static str =
|
||||||
|
if cfg!(target_os = "linux") { " create_pidfd: false,\n" } else { "" };
|
||||||
|
|
||||||
|
let mut command = Command::new("some-boring-name");
|
||||||
|
|
||||||
|
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
format!("{command:#?}"),
|
||||||
|
format!(
|
||||||
|
r#"Command {{
|
||||||
|
program: "some-boring-name",
|
||||||
|
args: [
|
||||||
|
"some-boring-name",
|
||||||
|
],
|
||||||
|
{PIDFD}}}"#
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
command.args(&["1", "2", "3"]);
|
||||||
|
|
||||||
|
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
format!("{command:#?}"),
|
||||||
|
format!(
|
||||||
|
r#"Command {{
|
||||||
|
program: "some-boring-name",
|
||||||
|
args: [
|
||||||
|
"some-boring-name",
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
],
|
||||||
|
{PIDFD}}}"#
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
format!("{command:?}"),
|
||||||
|
format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
format!("{command:#?}"),
|
||||||
|
format!(
|
||||||
|
r#"Command {{
|
||||||
|
program: "some-boring-name",
|
||||||
|
args: [
|
||||||
|
"exciting-name",
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
],
|
||||||
|
{PIDFD}}}"#
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut command_with_env_and_cwd = Command::new("boring-name");
|
||||||
|
command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar");
|
||||||
|
assert_eq!(
|
||||||
|
format!("{command_with_env_and_cwd:?}"),
|
||||||
|
r#"cd "/some/path" && FOO="bar" "boring-name""#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format!("{command_with_env_and_cwd:#?}"),
|
||||||
|
format!(
|
||||||
|
r#"Command {{
|
||||||
|
program: "boring-name",
|
||||||
|
args: [
|
||||||
|
"boring-name",
|
||||||
|
],
|
||||||
|
env: CommandEnv {{
|
||||||
|
clear: false,
|
||||||
|
vars: {{
|
||||||
|
"FOO": Some(
|
||||||
|
"bar",
|
||||||
|
),
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
cwd: Some(
|
||||||
|
"/some/path",
|
||||||
|
),
|
||||||
|
{PIDFD}}}"#
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// See issue #91991
|
// See issue #91991
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -144,6 +144,7 @@ pub enum ChildStdio {
|
|||||||
Null,
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Stdio {
|
pub enum Stdio {
|
||||||
Inherit,
|
Inherit,
|
||||||
Null,
|
Null,
|
||||||
@ -510,16 +511,68 @@ pub fn fd(&self) -> Option<c_int> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Command {
|
impl fmt::Debug for Command {
|
||||||
|
// show all attributes but `self.closures` which does not implement `Debug`
|
||||||
|
// and `self.argv` which is not useful for debugging
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if self.program != self.args[0] {
|
if f.alternate() {
|
||||||
write!(f, "[{:?}] ", self.program)?;
|
let mut debug_command = f.debug_struct("Command");
|
||||||
}
|
debug_command.field("program", &self.program).field("args", &self.args);
|
||||||
write!(f, "{:?}", self.args[0])?;
|
if !self.env.is_unchanged() {
|
||||||
|
debug_command.field("env", &self.env);
|
||||||
|
}
|
||||||
|
|
||||||
for arg in &self.args[1..] {
|
if self.cwd.is_some() {
|
||||||
write!(f, " {:?}", arg)?;
|
debug_command.field("cwd", &self.cwd);
|
||||||
|
}
|
||||||
|
if self.uid.is_some() {
|
||||||
|
debug_command.field("uid", &self.uid);
|
||||||
|
}
|
||||||
|
if self.gid.is_some() {
|
||||||
|
debug_command.field("gid", &self.gid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.groups.is_some() {
|
||||||
|
debug_command.field("groups", &self.groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.stdin.is_some() {
|
||||||
|
debug_command.field("stdin", &self.stdin);
|
||||||
|
}
|
||||||
|
if self.stdout.is_some() {
|
||||||
|
debug_command.field("stdout", &self.stdout);
|
||||||
|
}
|
||||||
|
if self.stderr.is_some() {
|
||||||
|
debug_command.field("stderr", &self.stderr);
|
||||||
|
}
|
||||||
|
if self.pgroup.is_some() {
|
||||||
|
debug_command.field("pgroup", &self.pgroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
debug_command.field("create_pidfd", &self.create_pidfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_command.finish()
|
||||||
|
} else {
|
||||||
|
if let Some(ref cwd) = self.cwd {
|
||||||
|
write!(f, "cd {cwd:?} && ")?;
|
||||||
|
}
|
||||||
|
for (key, value_opt) in self.get_envs() {
|
||||||
|
if let Some(value) = value_opt {
|
||||||
|
write!(f, "{}={value:?} ", key.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.program != self.args[0] {
|
||||||
|
write!(f, "[{:?}] ", self.program)?;
|
||||||
|
}
|
||||||
|
write!(f, "{:?}", self.args[0])?;
|
||||||
|
|
||||||
|
for arg in &self.args[1..] {
|
||||||
|
write!(f, " {:?}", arg)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
use crate::collections::BTreeMap;
|
use crate::collections::BTreeMap;
|
||||||
use crate::env;
|
use crate::env;
|
||||||
use crate::ffi::{OsStr, OsString};
|
use crate::ffi::{OsStr, OsString};
|
||||||
|
use crate::fmt;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::sys::pipe::read2;
|
use crate::sys::pipe::read2;
|
||||||
use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
|
use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
|
||||||
|
|
||||||
// Stores a set of changes to an environment
|
// Stores a set of changes to an environment
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub struct CommandEnv {
|
pub struct CommandEnv {
|
||||||
clear: bool,
|
clear: bool,
|
||||||
saw_path: bool,
|
saw_path: bool,
|
||||||
@ -22,6 +23,14 @@ fn default() -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for CommandEnv {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut debug_command_env = f.debug_struct("CommandEnv");
|
||||||
|
debug_command_env.field("clear", &self.clear).field("vars", &self.vars);
|
||||||
|
debug_command_env.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CommandEnv {
|
impl CommandEnv {
|
||||||
// Capture the current environment with these changes applied
|
// Capture the current environment with these changes applied
|
||||||
pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
|
pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
// run-pass
|
|
||||||
|
|
||||||
// ignore-windows - this is a unix-specific test
|
|
||||||
// ignore-emscripten no processes
|
|
||||||
// ignore-sgx no processes
|
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut command = Command::new("some-boring-name");
|
|
||||||
|
|
||||||
assert_eq!(format!("{:?}", command), r#""some-boring-name""#);
|
|
||||||
|
|
||||||
command.args(&["1", "2", "3"]);
|
|
||||||
|
|
||||||
assert_eq!(format!("{:?}", command), r#""some-boring-name" "1" "2" "3""#);
|
|
||||||
|
|
||||||
command.arg0("exciting-name");
|
|
||||||
|
|
||||||
assert_eq!(format!("{:?}", command), r#"["some-boring-name"] "exciting-name" "1" "2" "3""#);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user