Skip to content

Commit

Permalink
More verbose Debug implementation of std::process:Command
Browse files Browse the repository at this point in the history
based on commit: zackmdavis/rust@ccc019a from https://github.com/zackmdavis

close rust-lang/rust#42200

Add env variables and cwd to the shell-like debug output.

Also use the alternate syntax to display a more verbose display, while not showing internal fields and hiding fields when they have their default value.
  • Loading branch information
kraktus committed Dec 27, 2022
1 parent 50f0416 commit 79d1e1a
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 8 deletions.
9 changes: 9 additions & 0 deletions std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,15 @@ impl fmt::Debug for Command {
/// Format the program and arguments of a Command for display. Any
/// non-utf8 data is lossily converted using the utf8 replacement
/// 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 {
self.inner.fmt(f)
}
Expand Down
94 changes: 94 additions & 0 deletions std/src/process/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,100 @@ fn env_empty() {
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
#[test]
#[cfg(windows)]
Expand Down
67 changes: 60 additions & 7 deletions std/src/sys/unix/process/process_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pub enum ChildStdio {
Null,
}

#[derive(Debug)]
pub enum Stdio {
Inherit,
Null,
Expand Down Expand Up @@ -510,16 +511,68 @@ impl ChildStdio {
}

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 {
if self.program != self.args[0] {
write!(f, "[{:?}] ", self.program)?;
}
write!(f, "{:?}", self.args[0])?;
if f.alternate() {
let mut debug_command = f.debug_struct("Command");
debug_command.field("program", &self.program).field("args", &self.args);
if !self.env.is_unchanged() {
debug_command.field("env", &self.env);
}

if self.cwd.is_some() {
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);
}

for arg in &self.args[1..] {
write!(f, " {:?}", arg)?;
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(())
}
}

Expand Down
11 changes: 10 additions & 1 deletion std/src/sys_common/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
use crate::collections::BTreeMap;
use crate::env;
use crate::ffi::{OsStr, OsString};
use crate::fmt;
use crate::io;
use crate::sys::pipe::read2;
use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};

// Stores a set of changes to an environment
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct CommandEnv {
clear: bool,
saw_path: bool,
Expand All @@ -22,6 +23,14 @@ impl Default for CommandEnv {
}
}

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 {
// Capture the current environment with these changes applied
pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
Expand Down

0 comments on commit 79d1e1a

Please sign in to comment.