Rollup merge of #123633 - bjorn3:unsupported_command_data, r=jhpratt
Store all args in the unsupported Command implementation This allows printing them in the Debug impl as well as getting them again using the get_args() method. This allows programs that would normally spawn another process to more easily show which program they would have spawned if not for the fact that the target doesn't support spawning child processes without requiring intrusive changes to keep the args. For example rustc compiled to wasi will show the full linker invocation that would have been done.
This commit is contained in:
commit
a3f10a47d9
@ -1,7 +1,6 @@
|
|||||||
use crate::ffi::OsStr;
|
use crate::ffi::{OsStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::marker::PhantomData;
|
|
||||||
use crate::num::NonZero;
|
use crate::num::NonZero;
|
||||||
use crate::path::Path;
|
use crate::path::Path;
|
||||||
use crate::sys::fs::File;
|
use crate::sys::fs::File;
|
||||||
@ -16,7 +15,14 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub struct Command {
|
pub struct Command {
|
||||||
|
program: OsString,
|
||||||
|
args: Vec<OsString>,
|
||||||
env: CommandEnv,
|
env: CommandEnv,
|
||||||
|
|
||||||
|
cwd: Option<OsString>,
|
||||||
|
stdin: Option<Stdio>,
|
||||||
|
stdout: Option<Stdio>,
|
||||||
|
stderr: Option<Stdio>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// passed back to std::process with the pipes connected to the child, if any
|
// passed back to std::process with the pipes connected to the child, if any
|
||||||
@ -27,39 +33,62 @@ pub struct StdioPipes {
|
|||||||
pub stderr: Option<AnonPipe>,
|
pub stderr: Option<AnonPipe>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This should be a unit struct, so we can always construct it
|
#[derive(Debug)]
|
||||||
// The value here should be never used, since we cannot spawn processes.
|
|
||||||
pub enum Stdio {
|
pub enum Stdio {
|
||||||
Inherit,
|
Inherit,
|
||||||
Null,
|
Null,
|
||||||
MakePipe,
|
MakePipe,
|
||||||
|
ParentStdout,
|
||||||
|
ParentStderr,
|
||||||
|
#[allow(dead_code)] // This variant exists only for the Debug impl
|
||||||
|
InheritFile(File),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
pub fn new(_program: &OsStr) -> Command {
|
pub fn new(program: &OsStr) -> Command {
|
||||||
Command { env: Default::default() }
|
Command {
|
||||||
|
program: program.to_owned(),
|
||||||
|
args: vec![program.to_owned()],
|
||||||
|
env: Default::default(),
|
||||||
|
cwd: None,
|
||||||
|
stdin: None,
|
||||||
|
stdout: None,
|
||||||
|
stderr: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arg(&mut self, _arg: &OsStr) {}
|
pub fn arg(&mut self, arg: &OsStr) {
|
||||||
|
self.args.push(arg.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn env_mut(&mut self) -> &mut CommandEnv {
|
pub fn env_mut(&mut self) -> &mut CommandEnv {
|
||||||
&mut self.env
|
&mut self.env
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cwd(&mut self, _dir: &OsStr) {}
|
pub fn cwd(&mut self, dir: &OsStr) {
|
||||||
|
self.cwd = Some(dir.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stdin(&mut self, _stdin: Stdio) {}
|
pub fn stdin(&mut self, stdin: Stdio) {
|
||||||
|
self.stdin = Some(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stdout(&mut self, _stdout: Stdio) {}
|
pub fn stdout(&mut self, stdout: Stdio) {
|
||||||
|
self.stdout = Some(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stderr(&mut self, _stderr: Stdio) {}
|
pub fn stderr(&mut self, stderr: Stdio) {
|
||||||
|
self.stderr = Some(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_program(&self) -> &OsStr {
|
pub fn get_program(&self) -> &OsStr {
|
||||||
panic!("unsupported")
|
&self.program
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_args(&self) -> CommandArgs<'_> {
|
pub fn get_args(&self) -> CommandArgs<'_> {
|
||||||
CommandArgs { _p: PhantomData }
|
let mut iter = self.args.iter();
|
||||||
|
iter.next();
|
||||||
|
CommandArgs { iter }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_envs(&self) -> CommandEnvs<'_> {
|
pub fn get_envs(&self) -> CommandEnvs<'_> {
|
||||||
@ -67,7 +96,7 @@ pub fn get_envs(&self) -> CommandEnvs<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_dir(&self) -> Option<&Path> {
|
pub fn get_current_dir(&self) -> Option<&Path> {
|
||||||
None
|
self.cwd.as_ref().map(|cs| Path::new(cs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(
|
pub fn spawn(
|
||||||
@ -91,31 +120,83 @@ fn from(pipe: AnonPipe) -> Stdio {
|
|||||||
|
|
||||||
impl From<io::Stdout> for Stdio {
|
impl From<io::Stdout> for Stdio {
|
||||||
fn from(_: io::Stdout) -> Stdio {
|
fn from(_: io::Stdout) -> Stdio {
|
||||||
// FIXME: This is wrong.
|
Stdio::ParentStdout
|
||||||
// Instead, the Stdio we have here should be a unit struct.
|
|
||||||
panic!("unsupported")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Stderr> for Stdio {
|
impl From<io::Stderr> for Stdio {
|
||||||
fn from(_: io::Stderr) -> Stdio {
|
fn from(_: io::Stderr) -> Stdio {
|
||||||
// FIXME: This is wrong.
|
Stdio::ParentStderr
|
||||||
// Instead, the Stdio we have here should be a unit struct.
|
|
||||||
panic!("unsupported")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<File> for Stdio {
|
impl From<File> for Stdio {
|
||||||
fn from(_file: File) -> Stdio {
|
fn from(file: File) -> Stdio {
|
||||||
// FIXME: This is wrong.
|
Stdio::InheritFile(file)
|
||||||
// Instead, the Stdio we have here should be a unit struct.
|
|
||||||
panic!("unsupported")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Command {
|
impl fmt::Debug for Command {
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
// show all attributes
|
||||||
Ok(())
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
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.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_command.finish()
|
||||||
|
} else {
|
||||||
|
if let Some(ref cwd) = self.cwd {
|
||||||
|
write!(f, "cd {cwd:?} && ")?;
|
||||||
|
}
|
||||||
|
if self.env.does_clear() {
|
||||||
|
write!(f, "env -i ")?;
|
||||||
|
// Altered env vars will be printed next, that should exactly work as expected.
|
||||||
|
} else {
|
||||||
|
// Removed env vars need the command to be wrapped in `env`.
|
||||||
|
let mut any_removed = false;
|
||||||
|
for (key, value_opt) in self.get_envs() {
|
||||||
|
if value_opt.is_none() {
|
||||||
|
if !any_removed {
|
||||||
|
write!(f, "env ")?;
|
||||||
|
any_removed = true;
|
||||||
|
}
|
||||||
|
write!(f, "-u {} ", key.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Altered env vars can just be added in front of the program.
|
||||||
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,23 +298,30 @@ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandArgs<'a> {
|
pub struct CommandArgs<'a> {
|
||||||
_p: PhantomData<&'a ()>,
|
iter: crate::slice::Iter<'a, OsString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for CommandArgs<'a> {
|
impl<'a> Iterator for CommandArgs<'a> {
|
||||||
type Item = &'a OsStr;
|
type Item = &'a OsStr;
|
||||||
fn next(&mut self) -> Option<&'a OsStr> {
|
fn next(&mut self) -> Option<&'a OsStr> {
|
||||||
None
|
self.iter.next().map(|os| &**os)
|
||||||
}
|
}
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
(0, Some(0))
|
self.iter.size_hint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for CommandArgs<'a> {}
|
impl<'a> ExactSizeIterator for CommandArgs<'a> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.iter.len()
|
||||||
|
}
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.iter.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Debug for CommandArgs<'a> {
|
impl<'a> fmt::Debug for CommandArgs<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_list().finish()
|
f.debug_list().entries(self.iter.clone()).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user