Skip to content

Commit 92c1937

Browse files
committed
Auto merge of #97176 - kraktus:cmd_debug, r=the8472
More verbose `Debug` implementation of `std::process:Command` Mainly based on commit: zackmdavis@ccc019a from https://github.com/zackmdavis close #42200
2 parents db79625 + eb63dea commit 92c1937

File tree

5 files changed

+173
-29
lines changed

5 files changed

+173
-29
lines changed

library/std/src/process.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,15 @@ impl fmt::Debug for Command {
10381038
/// Format the program and arguments of a Command for display. Any
10391039
/// non-utf8 data is lossily converted using the utf8 replacement
10401040
/// character.
1041+
///
1042+
/// The default format approximates a shell invocation of the program along with its
1043+
/// arguments. It does not include most of the other command properties. The output is not guaranteed to work
1044+
/// (e.g. due to lack of shell-escaping or differences in path resolution)
1045+
/// On some platforms you can use [the alternate syntax] to show more fields.
1046+
///
1047+
/// Note that the debug implementation is platform-specific.
1048+
///
1049+
/// [the alternate syntax]: fmt#sign0
10411050
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10421051
self.inner.fmt(f)
10431052
}

library/std/src/process/tests.rs

+94
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,100 @@ fn env_empty() {
417417
assert!(p.is_ok());
418418
}
419419

420+
#[test]
421+
#[cfg(not(windows))]
422+
#[cfg_attr(any(target_os = "emscripten", target_env = "sgx"), ignore)]
423+
fn main() {
424+
const PIDFD: &'static str =
425+
if cfg!(target_os = "linux") { " create_pidfd: false,\n" } else { "" };
426+
427+
let mut command = Command::new("some-boring-name");
428+
429+
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name""#));
430+
431+
assert_eq!(
432+
format!("{command:#?}"),
433+
format!(
434+
r#"Command {{
435+
program: "some-boring-name",
436+
args: [
437+
"some-boring-name",
438+
],
439+
{PIDFD}}}"#
440+
)
441+
);
442+
443+
command.args(&["1", "2", "3"]);
444+
445+
assert_eq!(format!("{command:?}"), format!(r#""some-boring-name" "1" "2" "3""#));
446+
447+
assert_eq!(
448+
format!("{command:#?}"),
449+
format!(
450+
r#"Command {{
451+
program: "some-boring-name",
452+
args: [
453+
"some-boring-name",
454+
"1",
455+
"2",
456+
"3",
457+
],
458+
{PIDFD}}}"#
459+
)
460+
);
461+
462+
crate::os::unix::process::CommandExt::arg0(&mut command, "exciting-name");
463+
464+
assert_eq!(
465+
format!("{command:?}"),
466+
format!(r#"["some-boring-name"] "exciting-name" "1" "2" "3""#)
467+
);
468+
469+
assert_eq!(
470+
format!("{command:#?}"),
471+
format!(
472+
r#"Command {{
473+
program: "some-boring-name",
474+
args: [
475+
"exciting-name",
476+
"1",
477+
"2",
478+
"3",
479+
],
480+
{PIDFD}}}"#
481+
)
482+
);
483+
484+
let mut command_with_env_and_cwd = Command::new("boring-name");
485+
command_with_env_and_cwd.current_dir("/some/path").env("FOO", "bar");
486+
assert_eq!(
487+
format!("{command_with_env_and_cwd:?}"),
488+
r#"cd "/some/path" && FOO="bar" "boring-name""#
489+
);
490+
assert_eq!(
491+
format!("{command_with_env_and_cwd:#?}"),
492+
format!(
493+
r#"Command {{
494+
program: "boring-name",
495+
args: [
496+
"boring-name",
497+
],
498+
env: CommandEnv {{
499+
clear: false,
500+
vars: {{
501+
"FOO": Some(
502+
"bar",
503+
),
504+
}},
505+
}},
506+
cwd: Some(
507+
"/some/path",
508+
),
509+
{PIDFD}}}"#
510+
)
511+
);
512+
}
513+
420514
// See issue #91991
421515
#[test]
422516
#[cfg(windows)]

library/std/src/sys/unix/process/process_common.rs

+60-7
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ pub enum ChildStdio {
144144
Null,
145145
}
146146

147+
#[derive(Debug)]
147148
pub enum Stdio {
148149
Inherit,
149150
Null,
@@ -510,16 +511,68 @@ impl ChildStdio {
510511
}
511512

512513
impl fmt::Debug for Command {
514+
// show all attributes but `self.closures` which does not implement `Debug`
515+
// and `self.argv` which is not useful for debugging
513516
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514-
if self.program != self.args[0] {
515-
write!(f, "[{:?}] ", self.program)?;
516-
}
517-
write!(f, "{:?}", self.args[0])?;
517+
if f.alternate() {
518+
let mut debug_command = f.debug_struct("Command");
519+
debug_command.field("program", &self.program).field("args", &self.args);
520+
if !self.env.is_unchanged() {
521+
debug_command.field("env", &self.env);
522+
}
523+
524+
if self.cwd.is_some() {
525+
debug_command.field("cwd", &self.cwd);
526+
}
527+
if self.uid.is_some() {
528+
debug_command.field("uid", &self.uid);
529+
}
530+
if self.gid.is_some() {
531+
debug_command.field("gid", &self.gid);
532+
}
533+
534+
if self.groups.is_some() {
535+
debug_command.field("groups", &self.groups);
536+
}
537+
538+
if self.stdin.is_some() {
539+
debug_command.field("stdin", &self.stdin);
540+
}
541+
if self.stdout.is_some() {
542+
debug_command.field("stdout", &self.stdout);
543+
}
544+
if self.stderr.is_some() {
545+
debug_command.field("stderr", &self.stderr);
546+
}
547+
if self.pgroup.is_some() {
548+
debug_command.field("pgroup", &self.pgroup);
549+
}
550+
551+
#[cfg(target_os = "linux")]
552+
{
553+
debug_command.field("create_pidfd", &self.create_pidfd);
554+
}
518555

519-
for arg in &self.args[1..] {
520-
write!(f, " {:?}", arg)?;
556+
debug_command.finish()
557+
} else {
558+
if let Some(ref cwd) = self.cwd {
559+
write!(f, "cd {cwd:?} && ")?;
560+
}
561+
for (key, value_opt) in self.get_envs() {
562+
if let Some(value) = value_opt {
563+
write!(f, "{}={value:?} ", key.to_string_lossy())?;
564+
}
565+
}
566+
if self.program != self.args[0] {
567+
write!(f, "[{:?}] ", self.program)?;
568+
}
569+
write!(f, "{:?}", self.args[0])?;
570+
571+
for arg in &self.args[1..] {
572+
write!(f, " {:?}", arg)?;
573+
}
574+
Ok(())
521575
}
522-
Ok(())
523576
}
524577
}
525578

library/std/src/sys_common/process.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
use crate::collections::BTreeMap;
55
use crate::env;
66
use crate::ffi::{OsStr, OsString};
7+
use crate::fmt;
78
use crate::io;
89
use crate::sys::pipe::read2;
910
use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
1011

1112
// Stores a set of changes to an environment
12-
#[derive(Clone, Debug)]
13+
#[derive(Clone)]
1314
pub struct CommandEnv {
1415
clear: bool,
1516
saw_path: bool,
@@ -22,6 +23,14 @@ impl Default for CommandEnv {
2223
}
2324
}
2425

26+
impl fmt::Debug for CommandEnv {
27+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28+
let mut debug_command_env = f.debug_struct("CommandEnv");
29+
debug_command_env.field("clear", &self.clear).field("vars", &self.vars);
30+
debug_command_env.finish()
31+
}
32+
}
33+
2534
impl CommandEnv {
2635
// Capture the current environment with these changes applied
2736
pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {

src/test/ui/command/command-argv0-debug.rs

-21
This file was deleted.

0 commit comments

Comments
 (0)