Skip to content

Commit 559421e

Browse files
committed
Auto merge of rust-lang#114590 - ijackson:stdio-stdio-2, r=dtolnay
Allow redirecting subprocess stdout to our stderr etc. (redux) This is the code from rust-lang#88561, tidied up, including review suggestions, and with the for-testing-only CI commit removed. FCP for the API completed in rust-lang#88561. I have made a new MR to facilitate review. The discussion there is very cluttered and the branch is full of changes (in many cases as a result of changes to other Rust stdlib APIs since then). Assuming this MR is approvedl we should close that one. ### Reviewer doing a de novo review Just code review these four commits.. FCP discussion starts here: rust-lang#88561 (comment) Portability tests: you can see that this branch works on Windows too by looking at the CI results in rust-lang#88561, which has the same code changes as this branch but with an additional "DO NOT MERGE" commit to make the Windows tests run. ### Reviewer doing an incremental review from some version of rust-lang#88561 Review the new commits since your last review. I haven't force pushed the branch there. git diff the two branches (eg `git diff 1768861..0842b69`). You'll see that the only difference is in gitlab CI files. You can also see that *this* MR doesn't touch those files.
2 parents 37eec5c + 436fe01 commit 559421e

File tree

4 files changed

+134
-11
lines changed

4 files changed

+134
-11
lines changed

library/std/src/process.rs

+60
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,66 @@ impl From<fs::File> for Stdio {
15011501
}
15021502
}
15031503

1504+
#[stable(feature = "stdio_from_stdio", since = "CURRENT_RUSTC_VERSION")]
1505+
impl From<io::Stdout> for Stdio {
1506+
/// Redirect command stdout/stderr to our stdout
1507+
///
1508+
/// # Examples
1509+
///
1510+
/// ```rust
1511+
/// #![feature(exit_status_error)]
1512+
/// use std::io;
1513+
/// use std::process::Command;
1514+
///
1515+
/// # fn test() -> Result<(), Box<dyn std::error::Error>> {
1516+
/// let output = Command::new("whoami")
1517+
// "whoami" is a command which exists on both Unix and Windows,
1518+
// and which succeeds, producing some stdout output but no stderr.
1519+
/// .stdout(io::stdout())
1520+
/// .output()?;
1521+
/// output.status.exit_ok()?;
1522+
/// assert!(output.stdout.is_empty());
1523+
/// # Ok(())
1524+
/// # }
1525+
/// #
1526+
/// # if cfg!(unix) {
1527+
/// # test().unwrap();
1528+
/// # }
1529+
/// ```
1530+
fn from(inherit: io::Stdout) -> Stdio {
1531+
Stdio::from_inner(inherit.into())
1532+
}
1533+
}
1534+
1535+
#[stable(feature = "stdio_from_stdio", since = "CURRENT_RUSTC_VERSION")]
1536+
impl From<io::Stderr> for Stdio {
1537+
/// Redirect command stdout/stderr to our stderr
1538+
///
1539+
/// # Examples
1540+
///
1541+
/// ```rust
1542+
/// #![feature(exit_status_error)]
1543+
/// use std::io;
1544+
/// use std::process::Command;
1545+
///
1546+
/// # fn test() -> Result<(), Box<dyn std::error::Error>> {
1547+
/// let output = Command::new("whoami")
1548+
/// .stdout(io::stderr())
1549+
/// .output()?;
1550+
/// output.status.exit_ok()?;
1551+
/// assert!(output.stdout.is_empty());
1552+
/// # Ok(())
1553+
/// # }
1554+
/// #
1555+
/// # if cfg!(unix) {
1556+
/// # test().unwrap();
1557+
/// # }
1558+
/// ```
1559+
fn from(inherit: io::Stderr) -> Stdio {
1560+
Stdio::from_inner(inherit.into())
1561+
}
1562+
}
1563+
15041564
/// Describes the result of a process after it has terminated.
15051565
///
15061566
/// This `struct` is used to represent the exit status or other termination of a child process.

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

+29-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::sys::fd::FileDesc;
1313
use crate::sys::fs::File;
1414
use crate::sys::pipe::{self, AnonPipe};
1515
use crate::sys_common::process::{CommandEnv, CommandEnvs};
16-
use crate::sys_common::IntoInner;
16+
use crate::sys_common::{FromInner, IntoInner};
1717

1818
#[cfg(not(target_os = "fuchsia"))]
1919
use crate::sys::fs::OpenOptions;
@@ -150,6 +150,7 @@ pub enum Stdio {
150150
Null,
151151
MakePipe,
152152
Fd(FileDesc),
153+
StaticFd(BorrowedFd<'static>),
153154
}
154155

155156
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -463,6 +464,11 @@ impl Stdio {
463464
}
464465
}
465466

467+
Stdio::StaticFd(fd) => {
468+
let fd = FileDesc::from_inner(fd.try_clone_to_owned()?);
469+
Ok((ChildStdio::Owned(fd), None))
470+
}
471+
466472
Stdio::MakePipe => {
467473
let (reader, writer) = pipe::anon_pipe()?;
468474
let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) };
@@ -497,6 +503,28 @@ impl From<File> for Stdio {
497503
}
498504
}
499505

506+
impl From<io::Stdout> for Stdio {
507+
fn from(_: io::Stdout) -> Stdio {
508+
// This ought really to be is Stdio::StaticFd(input_argument.as_fd()).
509+
// But AsFd::as_fd takes its argument by reference, and yields
510+
// a bounded lifetime, so it's no use here. There is no AsStaticFd.
511+
//
512+
// Additionally AsFd is only implemented for the *locked* versions.
513+
// We don't want to lock them here. (The implications of not locking
514+
// are the same as those for process::Stdio::inherit().)
515+
//
516+
// Arguably the hypothetical AsStaticFd and AsFd<'static>
517+
// should be implemented for io::Stdout, not just for StdoutLocked.
518+
Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) })
519+
}
520+
}
521+
522+
impl From<io::Stderr> for Stdio {
523+
fn from(_: io::Stderr) -> Stdio {
524+
Stdio::StaticFd(unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) })
525+
}
526+
}
527+
500528
impl ChildStdio {
501529
pub fn fd(&self) -> Option<c_int> {
502530
match *self {

library/std/src/sys/unsupported/process.rs

+20
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ pub struct StdioPipes {
2727
pub stderr: Option<AnonPipe>,
2828
}
2929

30+
// FIXME: This should be a unit struct, so we can always construct it
31+
// The value here should be never used, since we cannot spawn processes.
3032
pub enum Stdio {
3133
Inherit,
3234
Null,
@@ -87,8 +89,26 @@ impl From<AnonPipe> for Stdio {
8789
}
8890
}
8991

92+
impl From<io::Stdout> for Stdio {
93+
fn from(_: io::Stdout) -> Stdio {
94+
// FIXME: This is wrong.
95+
// Instead, the Stdio we have here should be a unit struct.
96+
panic!("unsupported")
97+
}
98+
}
99+
100+
impl From<io::Stderr> for Stdio {
101+
fn from(_: io::Stderr) -> Stdio {
102+
// FIXME: This is wrong.
103+
// Instead, the Stdio we have here should be a unit struct.
104+
panic!("unsupported")
105+
}
106+
}
107+
90108
impl From<File> for Stdio {
91109
fn from(_file: File) -> Stdio {
110+
// FIXME: This is wrong.
111+
// Instead, the Stdio we have here should be a unit struct.
92112
panic!("unsupported")
93113
}
94114
}

library/std/src/sys/windows/process.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ pub struct Command {
172172

173173
pub enum Stdio {
174174
Inherit,
175+
InheritSpecific { from_stdio_id: c::DWORD },
175176
Null,
176177
MakePipe,
177178
Pipe(AnonPipe),
@@ -555,17 +556,19 @@ fn program_exists(path: &Path) -> Option<Vec<u16>> {
555556

556557
impl Stdio {
557558
fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> {
558-
match *self {
559-
Stdio::Inherit => match stdio::get_handle(stdio_id) {
560-
Ok(io) => unsafe {
561-
let io = Handle::from_raw_handle(io);
562-
let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
563-
io.into_raw_handle();
564-
ret
565-
},
566-
// If no stdio handle is available, then propagate the null value.
567-
Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) },
559+
let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) {
560+
Ok(io) => unsafe {
561+
let io = Handle::from_raw_handle(io);
562+
let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
563+
io.into_raw_handle();
564+
ret
568565
},
566+
// If no stdio handle is available, then propagate the null value.
567+
Err(..) => unsafe { Ok(Handle::from_raw_handle(ptr::null_mut())) },
568+
};
569+
match *self {
570+
Stdio::Inherit => use_stdio_id(stdio_id),
571+
Stdio::InheritSpecific { from_stdio_id } => use_stdio_id(from_stdio_id),
569572

570573
Stdio::MakePipe => {
571574
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
@@ -613,6 +616,18 @@ impl From<File> for Stdio {
613616
}
614617
}
615618

619+
impl From<io::Stdout> for Stdio {
620+
fn from(_: io::Stdout) -> Stdio {
621+
Stdio::InheritSpecific { from_stdio_id: c::STD_OUTPUT_HANDLE }
622+
}
623+
}
624+
625+
impl From<io::Stderr> for Stdio {
626+
fn from(_: io::Stderr) -> Stdio {
627+
Stdio::InheritSpecific { from_stdio_id: c::STD_ERROR_HANDLE }
628+
}
629+
}
630+
616631
////////////////////////////////////////////////////////////////////////////////
617632
// Processes
618633
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)