From a9585f03187316c619ddc11321a2a02cbd0d702d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 18 Aug 2019 19:13:37 +0200 Subject: [PATCH] tokio-process: change CommandExt to a fully asynchronous Command struct (#1448) Refs: #1371 --- tokio-process/src/lib.rs | 666 +++++++++++++++++++++-------- tokio-process/tests/issue_42.rs | 6 +- tokio-process/tests/smoke.rs | 4 +- tokio-process/tests/stdio.rs | 10 +- tokio-process/tests/support/mod.rs | 2 +- 5 files changed, 508 insertions(+), 180 deletions(-) diff --git a/tokio-process/src/lib.rs b/tokio-process/src/lib.rs index 42c5f386c..50b8b25e4 100644 --- a/tokio-process/src/lib.rs +++ b/tokio-process/src/lib.rs @@ -10,12 +10,11 @@ //! An implementation of asynchronous process management for Tokio. //! -//! This crate provides a `CommandExt` trait to enhance the functionality of the -//! `Command` type in the standard library. The three methods provided by this -//! trait mirror the "spawning" methods in the standard library. The -//! `CommandExt` trait in this crate, though, returns "future aware" types that -//! interoperate with Tokio. The asynchronous process support is provided -//! through signal handling on Unix and system APIs on Windows. +//! This crate provides a [Command](Command) struct that imitates the interface of the +//! [`std::process::Command`] type in the standard library, but provides asynchronous versions of +//! functions that create processes. These functions (`spawn`, `status`, `output` and their +//! variants) return "future aware" types that interoperate with Tokio. The asynchronous process +//! support is provided through signal handling on Unix and system APIs on Windows. //! //! # Examples //! @@ -25,15 +24,14 @@ //! ```no_run //! #![feature(async_await)] //! -//! use std::process::Command; -//! use tokio_process::CommandExt; +//! use tokio_process::Command; //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { -//! // Use the standard library's `Command` type to build a process and -//! // then execute it via the `CommandExt` trait. +//! // The usage is the same as with the standard library's `Command` type, however the value +//! // returned from `spawn` is a `Result` containing a `Future`. //! let child = Command::new("echo").arg("hello").arg("world") -//! .spawn_async(); +//! .spawn(); //! //! // Make sure our child succeeded in spawning and process the result //! let future = child.expect("failed to spawn"); @@ -51,15 +49,14 @@ //! ```no_run //! #![feature(async_await)] //! -//! use std::process::Command; -//! use tokio_process::CommandExt; +//! use tokio_process::Command; //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { -//! // Like above, but use `output_async` which returns a future instead of +//! // Like above, but use `output` which returns a future instead of //! // immediately returning the `Child`. //! let output = Command::new("echo").arg("hello").arg("world") -//! .output_async(); +//! .output(); //! //! let output = output.await?; //! @@ -75,9 +72,9 @@ //! #![feature(async_await)] //! //! use futures_util::stream::StreamExt; -//! use std::process::{Command, Stdio}; +//! use std::process::{Stdio}; //! use tokio::codec::{FramedRead, LinesCodec}; -//! use tokio_process::CommandExt; +//! use tokio_process::Command; //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { @@ -90,7 +87,7 @@ //! // the terminal if this process is invoked from the command line). //! cmd.stdout(Stdio::piped()); //! -//! let mut child = cmd.spawn_async() +//! let mut child = cmd.spawn() //! .expect("failed to spawn command"); //! //! let stdout = child.stdout().take() @@ -119,11 +116,11 @@ //! //! While similar to the standard library, this crate's `Child` type differs //! importantly in the behavior of `drop`. In the standard library, a child -//! process will continue running after the instance of `std::process::Child` -//! is dropped. In this crate, however, because `tokio_process::Child` is a +//! process will continue running after the instance of [`std::process::Child`] +//! is dropped. In this crate, however, because [`tokio_process::Child`](Child) is a //! future of the child's `ExitStatus`, a child process is terminated if //! `tokio_process::Child` is dropped. The behavior of the standard library can -//! be regained with the `Child::forget` method. +//! be regained with the [`Child::forget`] method. #[cfg(unix)] #[macro_use] @@ -132,22 +129,25 @@ extern crate lazy_static; #[macro_use] extern crate log; -use tokio_io::{AsyncRead, AsyncReadExt, AsyncWrite}; -use tokio_net::driver::Handle; - -use futures_core::future::TryFuture; -use futures_util::future; -use futures_util::future::FutureExt; -use futures_util::try_future::TryFutureExt; -use kill::Kill; +use std::ffi::OsStr; use std::fmt; use std::future::Future; use std::io; +use std::path::Path; use std::pin::Pin; -use std::process::{Command, ExitStatus, Output, Stdio}; +use std::process::{Command as StdCommand, ExitStatus, Output, Stdio}; use std::task::Context; use std::task::Poll; +use futures_core::TryFuture; +use futures_util::future; +use futures_util::future::FutureExt; +use futures_util::try_future::TryFutureExt; + +use kill::Kill; +use tokio_io::{AsyncRead, AsyncReadExt, AsyncWrite}; +use tokio_net::driver::Handle; + #[path = "unix/mod.rs"] #[cfg(unix)] mod imp; @@ -158,18 +158,312 @@ mod imp; mod kill; -/// Extensions provided by this crate to the `Command` type in the standard -/// library. +/// This structure mimics the API of [`std::process::Command`] found in the standard library, but +/// replaces functions that create a process with an asynchronous variant. The main provided +/// asynchronous functions are [spawn](Command::spawn), [status](Command::status), and +/// [output](Command::output). /// -/// This crate primarily enhances the standard library's `Command` type with -/// asynchronous capabilities. The currently three blocking functions in the -/// standard library, `spawn`, `status`, and `output`, all have asynchronous -/// versions through this trait. -/// -/// Note that the `Child` type spawned is specific to this crate, and that the -/// I/O handles created from this crate are all asynchronous as well (differing -/// from their `std` counterparts). -pub trait CommandExt { +/// `Command` uses asynchronous versions of some `std` types (for example [`Child`]). +#[derive(Debug)] +pub struct Command { + std: StdCommand, +} + +struct SpawnedChild { + child: imp::Child, + stdin: Option, + stdout: Option, + stderr: Option, +} + +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output` + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + /// + /// If `program` is not an absolute path, the `PATH` will be searched in + /// an OS-defined way. + /// + /// The search path to be used may be controlled by setting the + /// `PATH` environment variable on the Command, + /// but this has some implementation limitations on Windows + /// (see issue rust-lang/rust#37519). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// let command = Command::new("sh"); + /// ``` + pub fn new>(program: S) -> Command { + Command { + std: StdCommand::new(program), + } + } + + /// Adds an argument to pass to the program. + /// + /// Only one argument can be passed per use. So instead of: + /// + /// ```no_run + /// # tokio_process::Command::new("sh") + /// .arg("-C /path/to/repo") + /// # ; + /// ``` + /// + /// usage would be: + /// + /// ```no_run + /// # tokio_process::Command::new("sh") + /// .arg("-C") + /// .arg("/path/to/repo") + /// # ; + /// ``` + /// + /// To pass multiple arguments see [`args`]. + /// + /// [`args`]: #method.args + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .arg("-l") + /// .arg("-a"); + /// ``` + pub fn arg>(&mut self, arg: S) -> &mut Command { + self.std.arg(arg); + self + } + + /// Adds multiple arguments to pass to the program. + /// + /// To pass a single argument see [`arg`]. + /// + /// [`arg`]: #method.arg + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .args(&["-l", "-a"]); + /// ``` + pub fn args(&mut self, args: I) -> &mut Command + where + I: IntoIterator, + S: AsRef, + { + self.std.args(args); + self + } + + /// Inserts or updates an environment variable mapping. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .env("PATH", "/bin"); + /// ``` + pub fn env(&mut self, key: K, val: V) -> &mut Command + where + K: AsRef, + V: AsRef, + { + self.std.env(key, val); + self + } + + /// Adds or updates multiple environment variable mappings. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Stdio}; + /// use std::env; + /// use std::collections::HashMap; + /// use tokio_process::Command; + /// + /// let filtered_env : HashMap = + /// env::vars().filter(|&(ref k, _)| + /// k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" + /// ).collect(); + /// + /// let command = Command::new("printenv") + /// .stdin(Stdio::null()) + /// .stdout(Stdio::inherit()) + /// .env_clear() + /// .envs(&filtered_env); + /// ``` + pub fn envs(&mut self, vars: I) -> &mut Command + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + self.std.envs(vars); + self + } + + /// Removes an environment variable mapping. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .env_remove("PATH"); + /// ``` + pub fn env_remove>(&mut self, key: K) -> &mut Command { + self.std.env_remove(key); + self + } + + /// Clears the entire environment map for the child process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .env_clear(); + /// ``` + pub fn env_clear(&mut self) -> &mut Command { + self.std.env_clear(); + self + } + + /// Sets the working directory for the child process. + /// + /// # Platform-specific behavior + /// + /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous + /// whether it should be interpreted relative to the parent's working + /// directory or relative to `current_dir`. The behavior in this case is + /// platform specific and unstable, and it's recommended to use + /// [`canonicalize`] to get an absolute program path instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .current_dir("/bin"); + /// ``` + /// + /// [`canonicalize`]: ../fs/fn.canonicalize.html + pub fn current_dir>(&mut self, dir: P) -> &mut Command { + self.std.current_dir(dir); + self + } + + /// Configuration for the child process's standard input (stdin) handle. + /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: std::process::Stdio::inherit + /// [`piped`]: std::process::Stdio::piped + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Stdio}; + /// use tokio_process::Command; + /// + /// let command = Command::new("ls") + /// .stdin(Stdio::null()); + /// ``` + pub fn stdin>(&mut self, cfg: T) -> &mut Command { + self.std.stdin(cfg); + self + } + + /// Configuration for the child process's standard output (stdout) handle. + /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: std::process::Stdio::inherit + /// [`piped`]: std::process::Stdio::piped + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Stdio}; + /// use tokio_process::Command;; + /// + /// let command = Command::new("ls") + /// .stdout(Stdio::null()); + /// ``` + pub fn stdout>(&mut self, cfg: T) -> &mut Command { + self.std.stdout(cfg); + self + } + + /// Configuration for the child process's standard error (stderr) handle. + /// + /// Defaults to [`inherit`] when used with `spawn` or `status`, and + /// defaults to [`piped`] when used with `output`. + /// + /// [`inherit`]: std::process::Stdio::inherit + /// [`piped`]: std::process::Stdio::piped + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Stdio}; + /// use tokio_process::Command;; + /// + /// let command = Command::new("ls") + /// .stderr(Stdio::null()); + /// ``` + pub fn stderr>(&mut self, cfg: T) -> &mut Command { + self.std.stderr(cfg); + self + } + /// Executes the command as a child process, returning a handle to it. /// /// By default, stdin, stdout and stderr are inherited from the parent. @@ -182,8 +476,24 @@ pub trait CommandExt { /// /// All I/O this child does will be associated with the current default /// event loop. - fn spawn_async(&mut self) -> io::Result { - self.spawn_async_with_handle(&Handle::default()) + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// #![feature(async_await)] + /// use tokio_process::Command; + /// + /// async fn run_ls() -> std::process::ExitStatus { + /// Command::new("ls") + /// .spawn() + /// .expect("ls command failed to start") + /// .await + /// .expect("ls command failed to run") + /// } + pub fn spawn(&mut self) -> io::Result { + self.spawn_with_handle(&Handle::default()) } /// Executes the command as a child process, returning a handle to it. @@ -199,113 +509,8 @@ pub trait CommandExt { /// The `handle` specified to this method must be a handle to a valid event /// loop, and all I/O this child does will be associated with the specified /// event loop. - fn spawn_async_with_handle(&mut self, handle: &Handle) -> io::Result; - - /// Executes a command as a child process, waiting for it to finish and - /// collecting its exit status. - /// - /// By default, stdin, stdout and stderr are inherited from the parent. - /// - /// The `StatusAsync` future returned will resolve to the `ExitStatus` - /// type in the standard library representing how the process exited. If - /// any input/output handles are set to a pipe then they will be immediately - /// closed after the child is spawned. - /// - /// All I/O this child does will be associated with the current default - /// event loop. - /// - /// If the `StatusAsync` future is dropped before the future resolves, then - /// the child will be killed, if it was spawned. - /// - /// # Errors - /// - /// This function will return an error immediately if the child process - /// cannot be spawned. Otherwise errors obtained while waiting for the child - /// are returned through the `StatusAsync` future. - fn status_async(&mut self) -> io::Result { - self.status_async_with_handle(&Handle::default()) - } - - /// Executes a command as a child process, waiting for it to finish and - /// collecting its exit status. - /// - /// By default, stdin, stdout and stderr are inherited from the parent. - /// - /// The `StatusAsync` future returned will resolve to the `ExitStatus` - /// type in the standard library representing how the process exited. If - /// any input/output handles are set to a pipe then they will be immediately - /// closed after the child is spawned. - /// - /// The `handle` specified must be a handle to a valid event loop, and all - /// I/O this child does will be associated with the specified event loop. - /// - /// If the `StatusAsync` future is dropped before the future resolves, then - /// the child will be killed, if it was spawned. - /// - /// # Errors - /// - /// This function will return an error immediately if the child process - /// cannot be spawned. Otherwise errors obtained while waiting for the child - /// are returned through the `StatusAsync` future. - fn status_async_with_handle(&mut self, handle: &Handle) -> io::Result; - - /// Executes the command as a child process, waiting for it to finish and - /// collecting all of its output. - /// - /// > **Note**: this method, unlike the standard library, will - /// > unconditionally configure the stdout/stderr handles to be pipes, even - /// > if they have been previously configured. If this is not desired then - /// > the `spawn_async` method should be used in combination with the - /// > `wait_with_output` method on child. - /// - /// This method will return a future representing the collection of the - /// child process's stdout/stderr. The `OutputAsync` future will resolve to - /// the `Output` type in the standard library, containing `stdout` and - /// `stderr` as `Vec` along with an `ExitStatus` representing how the - /// process exited. - /// - /// All I/O this child does will be associated with the current default - /// event loop. - /// - /// If the `OutputAsync` future is dropped before the future resolves, then - /// the child will be killed, if it was spawned. - fn output_async(&mut self) -> OutputAsync { - self.output_async_with_handle(&Handle::default()) - } - - /// Executes the command as a child process, waiting for it to finish and - /// collecting all of its output. - /// - /// > **Note**: this method, unlike the standard library, will - /// > unconditionally configure the stdout/stderr handles to be pipes, even - /// > if they have been previously configured. If this is not desired then - /// > the `spawn_async` method should be used in combination with the - /// > `wait_with_output` method on child. - /// - /// This method will return a future representing the collection of the - /// child process's stdout/stderr. The `OutputAsync` future will resolve to - /// the `Output` type in the standard library, containing `stdout` and - /// `stderr` as `Vec` along with an `ExitStatus` representing how the - /// process exited. - /// - /// The `handle` specified must be a handle to a valid event loop, and all - /// I/O this child does will be associated with the specified event loop. - /// - /// If the `OutputAsync` future is dropped before the future resolves, then - /// the child will be killed, if it was spawned. - fn output_async_with_handle(&mut self, handle: &Handle) -> OutputAsync; -} - -struct SpawnedChild { - child: imp::Child, - stdin: Option, - stdout: Option, - stderr: Option, -} - -impl CommandExt for Command { - fn spawn_async_with_handle(&mut self, handle: &Handle) -> io::Result { - imp::spawn_child(self, handle).map(|spawned_child| Child { + pub fn spawn_with_handle(&mut self, handle: &Handle) -> io::Result { + imp::spawn_child(&mut self.std, handle).map(|spawned_child| Child { child: ChildDropGuard::new(spawned_child.child), stdin: spawned_child.stdin.map(|inner| ChildStdin { inner }), stdout: spawned_child.stdout.map(|inner| ChildStdout { inner }), @@ -313,8 +518,70 @@ impl CommandExt for Command { }) } - fn status_async_with_handle(&mut self, handle: &Handle) -> io::Result { - self.spawn_async_with_handle(handle).map(|mut child| { + /// Executes a command as a child process, waiting for it to finish and + /// collecting its exit status. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// The `StatusAsync` future returned will resolve to the `ExitStatus` + /// type in the standard library representing how the process exited. If + /// any input/output handles are set to a pipe then they will be immediately + /// closed after the child is spawned. + /// + /// All I/O this child does will be associated with the current default + /// event loop. + /// + /// If the `StatusAsync` future is dropped before the future resolves, then + /// the child will be killed, if it was spawned. + /// + /// # Errors + /// + /// This function will return an error immediately if the child process + /// cannot be spawned. Otherwise errors obtained while waiting for the child + /// are returned through the `StatusAsync` future. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// #![feature(async_await)] + /// use tokio_process::Command; + /// + /// async fn run_ls() -> std::process::ExitStatus { + /// Command::new("ls") + /// .status() + /// .expect("ls command failed to start") + /// .await + /// .expect("ls command failed to run") + /// } + pub fn status(&mut self) -> io::Result { + self.status_with_handle(&Handle::default()) + } + + /// Executes a command as a child process, waiting for it to finish and + /// collecting its exit status. + /// + /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// The `StatusAsync` future returned will resolve to the `ExitStatus` + /// type in the standard library representing how the process exited. If + /// any input/output handles are set to a pipe then they will be immediately + /// closed after the child is spawned. + /// + /// The `handle` specified must be a handle to a valid event loop, and all + /// I/O this child does will be associated with the specified event loop. + /// + /// If the `StatusAsync` future is dropped before the future resolves, then + /// the child will be killed, if it was spawned. + /// + /// # Errors + /// + /// This function will return an error immediately if the child process + /// cannot be spawned. Otherwise errors obtained while waiting for the child + /// are returned through the `StatusAsync` future. + pub fn status_with_handle(&mut self, handle: &Handle) -> io::Result { + self.spawn_with_handle(handle).map(|mut child| { // Ensure we close any stdio handles so we can't deadlock // waiting on the child which may be waiting to read/write // to a pipe we're holding. @@ -326,12 +593,71 @@ impl CommandExt for Command { }) } - fn output_async_with_handle(&mut self, handle: &Handle) -> OutputAsync { - self.stdout(Stdio::piped()); - self.stderr(Stdio::piped()); + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// > **Note**: this method, unlike the standard library, will + /// > unconditionally configure the stdout/stderr handles to be pipes, even + /// > if they have been previously configured. If this is not desired then + /// > the `spawn` method should be used in combination with the + /// > `wait_with_output` method on child. + /// + /// This method will return a future representing the collection of the + /// child process's stdout/stderr. The `OutputAsync` future will resolve to + /// the `Output` type in the standard library, containing `stdout` and + /// `stderr` as `Vec` along with an `ExitStatus` representing how the + /// process exited. + /// + /// All I/O this child does will be associated with the current default + /// event loop. + /// + /// If the `OutputAsync` future is dropped before the future resolves, then + /// the child will be killed, if it was spawned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// #![feature(async_await)] + /// use tokio_process::Command; + /// + /// async fn run_ls() { + /// let output: std::process::Output = Command::new("ls") + /// .output() + /// .await + /// .expect("ls command failed to run"); + /// println!("stderr of ls: {:?}", output.stderr); + /// } + pub fn output(&mut self) -> OutputAsync { + self.output_with_handle(&Handle::default()) + } - let inner = - future::ready(self.spawn_async_with_handle(handle)).and_then(Child::wait_with_output); + /// Executes the command as a child process, waiting for it to finish and + /// collecting all of its output. + /// + /// > **Note**: this method, unlike the standard library, will + /// > unconditionally configure the stdout/stderr handles to be pipes, even + /// > if they have been previously configured. If this is not desired then + /// > the `spawn` method should be used in combination with the + /// > `wait_with_output` method on child. + /// + /// This method will return a future representing the collection of the + /// child process's stdout/stderr. The `OutputAsync` future will resolve to + /// the `Output` type in the standard library, containing `stdout` and + /// `stderr` as `Vec` along with an `ExitStatus` representing how the + /// process exited. + /// + /// The `handle` specified must be a handle to a valid event loop, and all + /// I/O this child does will be associated with the specified event loop. + /// + /// If the `OutputAsync` future is dropped before the future resolves, then + /// the child will be killed, if it was spawned. + pub fn output_with_handle(&mut self, handle: &Handle) -> OutputAsync { + self.std.stdout(Stdio::piped()); + self.std.stderr(Stdio::piped()); + + let inner = future::ready(self.spawn_with_handle(handle)).and_then(Child::wait_with_output); OutputAsync { inner: inner.boxed(), @@ -514,13 +840,12 @@ impl Child { /// /// ```no_run /// # #![feature(async_await)] - /// # use std::process::Command; - /// # use tokio_process::CommandExt; + /// # use tokio_process::Command; /// /// # #[tokio::main] /// # async fn main() { /// let child = Command::new("echo").arg("hello").arg("world") - /// .spawn_async() + /// .spawn() /// .expect("failed to spawn"); /// /// tokio::spawn(async { @@ -540,7 +865,7 @@ impl Future for Child { } } -/// Future returned from the `Child::wait_with_output` method. +/// Future returned from the [`Child::wait_with_output`] method. /// /// This future will resolve to the standard library's `Output` type which /// contains the exit status, stdout, and stderr of a child process. @@ -565,7 +890,7 @@ impl Future for WaitWithOutput { } } -/// Future returned by the `CommandExt::status_async` method. +/// Future returned by the [`Command::status`] method. /// /// This future is used to conveniently spawn a child and simply wait for its /// exit status. This future will resolves to the `ExitStatus` type in the @@ -584,7 +909,7 @@ impl Future for StatusAsync { } } -/// Future returned by the `CommandExt::output_async` method. +/// Future returned by the [`Command::output`] method. /// /// This future is mostly equivalent to spawning a process and then calling /// `wait_with_output` on it internally. This can be useful to simply spawn a @@ -677,9 +1002,10 @@ impl AsyncRead for ChildStderr { #[cfg(unix)] mod sys { - use super::{ChildStderr, ChildStdin, ChildStdout}; use std::os::unix::io::{AsRawFd, RawFd}; + use super::{ChildStderr, ChildStdin, ChildStdout}; + impl AsRawFd for ChildStdin { fn as_raw_fd(&self) -> RawFd { self.inner.get_ref().as_raw_fd() @@ -701,9 +1027,10 @@ mod sys { #[cfg(windows)] mod sys { - use super::{ChildStderr, ChildStdin, ChildStdout}; use std::os::windows::io::{AsRawHandle, RawHandle}; + use super::{ChildStderr, ChildStdin, ChildStdout}; + impl AsRawHandle for ChildStdin { fn as_raw_handle(&self) -> RawHandle { self.inner.get_ref().as_raw_handle() @@ -725,15 +1052,18 @@ mod sys { #[cfg(test)] mod test { - use super::ChildDropGuard; - use crate::kill::Kill; - use futures_util::future::FutureExt; use std::future::Future; use std::io; use std::pin::Pin; use std::task::Context; use std::task::Poll; + use futures_util::future::FutureExt; + + use crate::kill::Kill; + + use super::ChildDropGuard; + struct Mock { num_kills: usize, num_polls: usize, diff --git a/tokio-process/tests/issue_42.rs b/tokio-process/tests/issue_42.rs index 48e734278..ea3397c33 100644 --- a/tokio-process/tests/issue_42.rs +++ b/tokio-process/tests/issue_42.rs @@ -4,13 +4,13 @@ use futures_util::future::FutureExt; use futures_util::stream::FuturesOrdered; use futures_util::stream::StreamExt; -use std::process::{Command, Stdio}; +use std::process::Stdio; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; use std::time::Duration; use tokio::runtime::current_thread; -use tokio_process::CommandExt; +use tokio_process::Command; mod support; @@ -27,7 +27,7 @@ fn run_test() { .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) - .spawn_async() + .spawn() .unwrap() .boxed(), ) diff --git a/tokio-process/tests/smoke.rs b/tokio-process/tests/smoke.rs index e9ac5f078..6cfd4f1cb 100644 --- a/tokio-process/tests/smoke.rs +++ b/tokio-process/tests/smoke.rs @@ -1,8 +1,6 @@ #![warn(rust_2018_idioms)] #![feature(async_await)] -use tokio_process::CommandExt; - mod support; #[tokio::test] @@ -10,7 +8,7 @@ async fn simple() { let mut cmd = support::cmd("exit"); cmd.arg("2"); - let mut child = cmd.spawn_async().unwrap(); + let mut child = cmd.spawn().unwrap(); let id = child.id(); assert!(id > 0); diff --git a/tokio-process/tests/stdio.rs b/tokio-process/tests/stdio.rs index 6eb5205a4..1cf7a4d5e 100644 --- a/tokio-process/tests/stdio.rs +++ b/tokio-process/tests/stdio.rs @@ -5,14 +5,14 @@ extern crate log; use std::io; -use std::process::{Command, ExitStatus, Stdio}; +use std::process::{ExitStatus, Stdio}; use futures_util::future; use futures_util::future::FutureExt; use futures_util::stream::StreamExt; use tokio::codec::{FramedRead, LinesCodec}; use tokio::io::AsyncWriteExt; -use tokio_process::{Child, CommandExt}; +use tokio_process::{Child, Command}; mod support; @@ -97,14 +97,14 @@ async fn feed_cat(mut cat: Child, n: usize) -> io::Result { /// - The child does produce EOF on stdout after the last line. #[tokio::test] async fn feed_a_lot() { - let child = cat().spawn_async().unwrap(); + let child = cat().spawn().unwrap(); let status = support::with_timeout(feed_cat(child, 10000)).await.unwrap(); assert_eq!(status.code(), Some(0)); } #[tokio::test] async fn wait_with_output_captures() { - let mut child = cat().spawn_async().unwrap(); + let mut child = cat().spawn().unwrap(); let mut stdin = child.stdin().take().unwrap(); let write_bytes = b"1234"; @@ -128,7 +128,7 @@ async fn status_closes_any_pipes() { // Cat will open a pipe between the parent and child. // If `status_async` doesn't ensure the handles are closed, // we would end up blocking forever (and time out). - let child = cat().status_async().expect("failed to spawn child"); + let child = cat().status().expect("failed to spawn child"); support::with_timeout(child) .await diff --git a/tokio-process/tests/support/mod.rs b/tokio-process/tests/support/mod.rs index 030fb58a2..9d4ca1c3e 100644 --- a/tokio-process/tests/support/mod.rs +++ b/tokio-process/tests/support/mod.rs @@ -4,9 +4,9 @@ use futures_util::future; use futures_util::future::FutureExt; use std::env; use std::future::Future; -use std::process::Command; use std::time::Duration; use tokio::timer::Timeout; +use tokio_process::Command; #[allow(dead_code)] pub fn cmd(s: &str) -> Command {