]> git.lizzy.rs Git - rust.git/commitdiff
Process::new etc should support non-utf8 commands/args
authorAaron Turon <aturon@mozilla.com>
Mon, 5 May 2014 21:33:55 +0000 (14:33 -0700)
committerAaron Turon <aturon@mozilla.com>
Thu, 15 May 2014 05:52:31 +0000 (22:52 -0700)
The existing APIs for spawning processes took strings for the command
and arguments, but the underlying system may not impose utf8 encoding,
so this is overly limiting.

The assumption we actually want to make is just that the command and
arguments are viewable as [u8] slices with no interior NULLs, i.e., as
CStrings. The ToCStr trait is a handy bound for types that meet this
requirement (such as &str and Path).

However, since the commands and arguments are often a mixture of
strings and paths, it would be inconvenient to take a slice with a
single T: ToCStr bound. So this patch revamps the process creation API
to instead use a builder-style interface, called `Command`, allowing
arguments to be added one at a time with differing ToCStr
implementations for each.

The initial cut of the builder API has some drawbacks that can be
addressed once issue #13851 (libstd as a facade) is closed. These are
detailed as FIXMEs.

Closes #11650.

[breaking-change]

27 files changed:
src/compiletest/procsrv.rs
src/compiletest/runtest.rs
src/libnative/io/mod.rs
src/libnative/io/process.rs
src/librustc/back/archive.rs
src/librustc/back/link.rs
src/librustdoc/test.rs
src/librustuv/process.rs
src/librustuv/uvio.rs
src/libstd/io/mod.rs
src/libstd/io/process.rs
src/libstd/rt/rtio.rs
src/libstd/unstable/dynamic_lib.rs
src/libworkcache/lib.rs
src/test/auxiliary/linkage-visibility.rs
src/test/run-make/unicode-input/multiple_files.rs
src/test/run-make/unicode-input/span_length.rs
src/test/run-pass/backtrace.rs
src/test/run-pass/core-run-destroy.rs
src/test/run-pass/issue-10626.rs
src/test/run-pass/issue-13304.rs
src/test/run-pass/logging-separate-lines.rs
src/test/run-pass/out-of-stack.rs
src/test/run-pass/process-detach.rs
src/test/run-pass/process-spawn-with-unicode-params.rs
src/test/run-pass/signal-exit-status.rs
src/test/run-pass/sigpipe-should-be-ignored.rs

index d3642a939db049a30c9c42a9ea561fe30b33a79c..27a31ea909fea35107a925ef508e828139ad8b35 100644 (file)
@@ -10,7 +10,7 @@
 
 use std::os;
 use std::str;
-use std::io::process::{ProcessExit, Process, ProcessConfig, ProcessOutput};
+use std::io::process::{ProcessExit, Command, Process, ProcessOutput};
 
 #[cfg(target_os = "win32")]
 fn target_env(lib_path: &str, prog: &str) -> Vec<(~str, ~str)> {
@@ -68,14 +68,7 @@ pub fn run(lib_path: &str,
            input: Option<~str>) -> Option<Result> {
 
     let env = env.clone().append(target_env(lib_path, prog).as_slice());
-    let opt_process = Process::configure(ProcessConfig {
-        program: prog,
-        args: args,
-        env: Some(env.as_slice()),
-        .. ProcessConfig::new()
-    });
-
-    match opt_process {
+    match Command::new(prog).args(args).env(env.as_slice()).spawn() {
         Ok(mut process) => {
             for input in input.iter() {
                 process.stdin.get_mut_ref().write(input.as_bytes()).unwrap();
@@ -100,14 +93,7 @@ pub fn run_background(lib_path: &str,
            input: Option<~str>) -> Option<Process> {
 
     let env = env.clone().append(target_env(lib_path, prog).as_slice());
-    let opt_process = Process::configure(ProcessConfig {
-        program: prog,
-        args: args,
-        env: Some(env.as_slice()),
-        .. ProcessConfig::new()
-    });
-
-    match opt_process {
+    match Command::new(prog).args(args).env(env.as_slice()).spawn() {
         Ok(mut process) => {
             for input in input.iter() {
                 process.stdin.get_mut_ref().write(input.as_bytes()).unwrap();
index dab1185435d9319c850a5e9ff1dcc95ab644f653..fbbfcf94eb11de3c01ec2dcaa5e7eb7505061543 100644 (file)
@@ -420,7 +420,7 @@ fn debugger() -> ~str { "gdb".to_owned() }
 }
 
 fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) {
-    use std::io::process::{Process, ProcessConfig, ProcessOutput};
+    use std::io::process::{Command, ProcessOutput};
 
     if config.lldb_python_dir.is_none() {
         fatal("Can't run LLDB test because LLDB's python path is not set.".to_owned());
@@ -483,25 +483,13 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
 
     fn run_lldb(config: &Config, test_executable: &Path, debugger_script: &Path) -> ProcRes {
         // Prepare the lldb_batchmode which executes the debugger script
-        let lldb_batchmode_script = "./src/etc/lldb_batchmode.py".to_owned();
-        let test_executable_str = test_executable.as_str().unwrap().to_owned();
-        let debugger_script_str = debugger_script.as_str().unwrap().to_owned();
-        let commandline = format!("python {} {} {}",
-                                  lldb_batchmode_script.as_slice(),
-                                  test_executable_str.as_slice(),
-                                  debugger_script_str.as_slice());
-
-        let args = &[lldb_batchmode_script, test_executable_str, debugger_script_str];
-        let env = &[("PYTHONPATH".to_owned(), config.lldb_python_dir.clone().unwrap())];
-
-        let opt_process = Process::configure(ProcessConfig {
-            program: "python",
-            args: args,
-            env: Some(env),
-            .. ProcessConfig::new()
-        });
-
-        let (status, out, err) = match opt_process {
+        let mut cmd = Command::new("python");
+        cmd.arg("./src/etc/lldb_batchmode.py")
+           .arg(test_executable)
+           .arg(debugger_script)
+           .env([("PYTHONPATH", config.lldb_python_dir.clone().unwrap().as_slice())]);
+
+        let (status, out, err) = match cmd.spawn() {
             Ok(process) => {
                 let ProcessOutput { status, output, error } =
                     process.wait_with_output().unwrap();
@@ -520,7 +508,7 @@ fn run_lldb(config: &Config, test_executable: &Path, debugger_script: &Path) ->
             status: status,
             stdout: out,
             stderr: err,
-            cmdline: commandline
+            cmdline: format!("{}", cmd)
         };
     }
 }
index a9aca656319efa7a4263131246c4f3412023c0ad..0c103bc4695fd7bbb1146e69b1f5bac49c2bb761 100644 (file)
 use std::io;
 use std::io::IoError;
 use std::io::net::ip::SocketAddr;
-use std::io::process::ProcessConfig;
 use std::io::signal::Signum;
 use std::os;
 use std::rt::rtio;
 use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket};
 use std::rt::rtio::{RtioUnixListener, RtioPipe, RtioFileStream, RtioProcess};
-use std::rt::rtio::{RtioSignal, RtioTTY, CloseBehavior, RtioTimer};
+use std::rt::rtio::{RtioSignal, RtioTTY, CloseBehavior, RtioTimer, ProcessConfig};
 use ai = std::io::net::addrinfo;
 
 // Local re-exports
@@ -258,10 +257,10 @@ fn fs_utime(&mut self, src: &CString, atime: u64,
     fn timer_init(&mut self) -> IoResult<Box<RtioTimer:Send>> {
         timer::Timer::new().map(|t| box t as Box<RtioTimer:Send>)
     }
-    fn spawn(&mut self, config: ProcessConfig)
+    fn spawn(&mut self, cfg: ProcessConfig)
             -> IoResult<(Box<RtioProcess:Send>,
                          Vec<Option<Box<RtioPipe:Send>>>)> {
-        process::Process::spawn(config).map(|(p, io)| {
+        process::Process::spawn(cfg).map(|(p, io)| {
             (box p as Box<RtioProcess:Send>,
              io.move_iter().map(|p| p.map(|p| {
                  box p as Box<RtioPipe:Send>
index 799db64e6889775878386af1bf26ceb6b5913662..0eebd3380e626c21953780729dd82e9e11ed3aa1 100644 (file)
 use std::os;
 use std::ptr;
 use std::rt::rtio;
+use std::rt::rtio::ProcessConfig;
+use std::c_str::CString;
 use p = std::io::process;
 
-
 use super::IoResult;
 use super::file;
 use super::util;
@@ -65,27 +66,11 @@ impl Process {
     /// Creates a new process using native process-spawning abilities provided
     /// by the OS. Operations on this process will be blocking instead of using
     /// the runtime for sleeping just this current task.
-    ///
-    /// # Arguments
-    ///
-    /// * prog - the program to run
-    /// * args - the arguments to pass to the program, not including the program
-    ///          itself
-    /// * env - an optional environment to specify for the child process. If
-    ///         this value is `None`, then the child will inherit the parent's
-    ///         environment
-    /// * cwd - an optionally specified current working directory of the child,
-    ///         defaulting to the parent's current working directory
-    /// * stdin, stdout, stderr - These optionally specified file descriptors
-    ///     dictate where the stdin/out/err of the child process will go. If
-    ///     these are `None`, then this module will bind the input/output to an
-    ///     os pipe instead. This process takes ownership of these file
-    ///     descriptors, closing them upon destruction of the process.
-    pub fn spawn(config: p::ProcessConfig)
+    pub fn spawn(cfg: ProcessConfig)
         -> Result<(Process, Vec<Option<file::FileDesc>>), io::IoError>
     {
         // right now we only handle stdin/stdout/stderr.
-        if config.extra_io.len() > 0 {
+        if cfg.extra_io.len() > 0 {
             return Err(super::unimpl());
         }
 
@@ -109,14 +94,11 @@ fn get_io(io: p::StdioContainer, ret: &mut Vec<Option<file::FileDesc>>)
         }
 
         let mut ret_io = Vec::new();
-        let (in_pipe, in_fd) = get_io(config.stdin, &mut ret_io);
-        let (out_pipe, out_fd) = get_io(config.stdout, &mut ret_io);
-        let (err_pipe, err_fd) = get_io(config.stderr, &mut ret_io);
+        let (in_pipe, in_fd) = get_io(cfg.stdin, &mut ret_io);
+        let (out_pipe, out_fd) = get_io(cfg.stdout, &mut ret_io);
+        let (err_pipe, err_fd) = get_io(cfg.stderr, &mut ret_io);
 
-        let env = config.env.map(|a| a.to_owned());
-        let cwd = config.cwd.map(|a| Path::new(a));
-        let res = spawn_process_os(config, env, cwd.as_ref(), in_fd, out_fd,
-                                   err_fd);
+        let res = spawn_process_os(cfg, in_fd, out_fd, err_fd);
 
         unsafe {
             for pipe in in_pipe.iter() { let _ = libc::close(pipe.input); }
@@ -262,11 +244,8 @@ struct SpawnProcessResult {
 }
 
 #[cfg(windows)]
-fn spawn_process_os(config: p::ProcessConfig,
-                    env: Option<~[(~str, ~str)]>,
-                    dir: Option<&Path>,
-                    in_fd: c_int, out_fd: c_int,
-                    err_fd: c_int) -> IoResult<SpawnProcessResult> {
+fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_int)
+                 -> IoResult<SpawnProcessResult> {
     use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
     use libc::consts::os::extra::{
         TRUE, FALSE,
@@ -284,7 +263,7 @@ fn spawn_process_os(config: p::ProcessConfig,
 
     use std::mem;
 
-    if config.gid.is_some() || config.uid.is_some() {
+    if cfg.gid.is_some() || cfg.uid.is_some() {
         return Err(io::IoError {
             kind: io::OtherIoError,
             desc: "unsupported gid/uid requested on windows",
@@ -293,7 +272,6 @@ fn spawn_process_os(config: p::ProcessConfig,
     }
 
     unsafe {
-
         let mut si = zeroed_startupinfo();
         si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
         si.dwFlags = STARTF_USESTDHANDLES;
@@ -333,23 +311,26 @@ fn spawn_process_os(config: p::ProcessConfig,
             }
         }
 
-        let cmd = make_command_line(config.program, config.args);
+        let cmd_str = make_command_line(cfg.program, cfg.args);
         let mut pi = zeroed_process_information();
         let mut create_err = None;
 
         // stolen from the libuv code.
         let mut flags = libc::CREATE_UNICODE_ENVIRONMENT;
-        if config.detach {
+        if cfg.detach {
             flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
         }
 
-        with_envp(env, |envp| {
-            with_dirp(dir, |dirp| {
-                os::win32::as_mut_utf16_p(cmd, |cmdp| {
-                    let created = CreateProcessW(ptr::null(), cmdp,
-                                                 ptr::mut_null(), ptr::mut_null(), TRUE,
-                                                 flags, envp, dirp, &mut si,
-                                                 &mut pi);
+        with_envp(cfg.env, |envp| {
+            with_dirp(cfg.cwd, |dirp| {
+                os::win32::as_mut_utf16_p(cmd_str, |cmdp| {
+                    let created = CreateProcessW(ptr::null(),
+                                                 cmdp,
+                                                 ptr::mut_null(),
+                                                 ptr::mut_null(),
+                                                 TRUE,
+                                                 flags, envp, dirp,
+                                                 &mut si, &mut pi);
                     if created == FALSE {
                         create_err = Some(super::last_error());
                     }
@@ -415,12 +396,14 @@ fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMA
 }
 
 #[cfg(windows)]
-fn make_command_line(prog: &str, args: &[~str]) -> ~str {
+fn make_command_line(prog: &CString, args: &[CString]) -> ~str {
     let mut cmd = StrBuf::new();
-    append_arg(&mut cmd, prog);
+    append_arg(&mut cmd, prog.as_str()
+                             .expect("expected program name to be utf-8 encoded"));
     for arg in args.iter() {
         cmd.push_char(' ');
-        append_arg(&mut cmd, *arg);
+        append_arg(&mut cmd, arg.as_str()
+                                .expect("expected argument to be utf-8 encoded"));
     }
     return cmd.into_owned();
 
@@ -468,11 +451,9 @@ fn backslash_run_ends_in_quote(s: &Vec<char>, mut i: uint) -> bool {
 }
 
 #[cfg(unix)]
-fn spawn_process_os(config: p::ProcessConfig,
-                    env: Option<~[(~str, ~str)]>,
-                    dir: Option<&Path>,
-                    in_fd: c_int, out_fd: c_int,
-                    err_fd: c_int) -> IoResult<SpawnProcessResult> {
+fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_int)
+                -> IoResult<SpawnProcessResult>
+{
     use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
     use libc::funcs::bsd44::getdtablesize;
     use io::c;
@@ -500,11 +481,10 @@ unsafe fn set_cloexec(fd: c_int) {
         assert_eq!(ret, 0);
     }
 
-    let dirp = dir.map(|p| p.to_c_str());
-    let dirp = dirp.as_ref().map(|c| c.with_ref(|p| p)).unwrap_or(ptr::null());
+    let dirp = cfg.cwd.map(|c| c.with_ref(|p| p)).unwrap_or(ptr::null());
 
-    with_envp(env, proc(envp) {
-        with_argv(config.program, config.args, proc(argv) unsafe {
+    with_envp(cfg.env, proc(envp) {
+        with_argv(cfg.program, cfg.args, proc(argv) unsafe {
             let pipe = os::pipe();
             let mut input = file::FileDesc::new(pipe.input, true);
             let mut output = file::FileDesc::new(pipe.out, true);
@@ -605,7 +585,7 @@ fn fail(output: &mut file::FileDesc) -> ! {
                 }
             }
 
-            match config.gid {
+            match cfg.gid {
                 Some(u) => {
                     if libc::setgid(u as libc::gid_t) != 0 {
                         fail(&mut output);
@@ -613,7 +593,7 @@ fn fail(output: &mut file::FileDesc) -> ! {
                 }
                 None => {}
             }
-            match config.uid {
+            match cfg.uid {
                 Some(u) => {
                     // When dropping privileges from root, the `setgroups` call will
                     // remove any extraneous groups. If we don't call this, then
@@ -633,7 +613,7 @@ fn setgroups(ngroups: libc::c_int,
                 }
                 None => {}
             }
-            if config.detach {
+            if cfg.detach {
                 // Don't check the error of setsid because it fails if we're the
                 // process leader already. We just forked so it shouldn't return
                 // error, but ignore it anyway.
@@ -652,47 +632,47 @@ fn setgroups(ngroups: libc::c_int,
 }
 
 #[cfg(unix)]
-fn with_argv<T>(prog: &str, args: &[~str], cb: proc(**libc::c_char) -> T) -> T {
-    // We can't directly convert `str`s into `*char`s, as someone needs to hold
-    // a reference to the intermediary byte buffers. So first build an array to
-    // hold all the ~[u8] byte strings.
-    let mut tmps = Vec::with_capacity(args.len() + 1);
-
-    tmps.push(prog.to_c_str());
-
-    for arg in args.iter() {
-        tmps.push(arg.to_c_str());
-    }
-
-    // Next, convert each of the byte strings into a pointer. This is
-    // technically unsafe as the caller could leak these pointers out of our
-    // scope.
-    let mut ptrs: Vec<_> = tmps.iter().map(|tmp| tmp.with_ref(|buf| buf)).collect();
-
-    // Finally, make sure we add a null pointer.
+fn with_argv<T>(prog: &CString, args: &[CString], cb: proc(**libc::c_char) -> T) -> T {
+    let mut ptrs: Vec<*libc::c_char> = Vec::with_capacity(args.len()+1);
+
+    // Convert the CStrings into an array of pointers. Note: the
+    // lifetime of the various CStrings involved is guaranteed to be
+    // larger than the lifetime of our invocation of cb, but this is
+    // technically unsafe as the callback could leak these pointers
+    // out of our scope.
+    ptrs.push(prog.with_ref(|buf| buf));
+    ptrs.extend(args.iter().map(|tmp| tmp.with_ref(|buf| buf)));
+
+    // Add a terminating null pointer (required by libc).
     ptrs.push(ptr::null());
 
     cb(ptrs.as_ptr())
 }
 
 #[cfg(unix)]
-fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: proc(*c_void) -> T) -> T {
+fn with_envp<T>(env: Option<&[(CString, CString)]>, cb: proc(*c_void) -> T) -> T {
     // On posixy systems we can pass a char** for envp, which is a
-    // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to
-    // have a temporary buffer to hold the intermediary `~[u8]` byte strings.
+    // null-terminated array of "k=v\0" strings. Since we must create
+    // these strings locally, yet expose a raw pointer to them, we
+    // create a temporary vector to own the CStrings that outlives the
+    // call to cb.
     match env {
         Some(env) => {
             let mut tmps = Vec::with_capacity(env.len());
 
             for pair in env.iter() {
-                let kv = format!("{}={}", *pair.ref0(), *pair.ref1());
-                tmps.push(kv.to_c_str());
+                let mut kv = Vec::new();
+                kv.push_all(pair.ref0().as_bytes_no_nul());
+                kv.push('=' as u8);
+                kv.push_all(pair.ref1().as_bytes()); // includes terminal \0
+                tmps.push(kv);
             }
 
-            // Once again, this is unsafe.
-            let mut ptrs: Vec<*libc::c_char> = tmps.iter()
-                                                   .map(|tmp| tmp.with_ref(|buf| buf))
-                                                   .collect();
+            // As with `with_argv`, this is unsafe, since cb could leak the pointers.
+            let mut ptrs: Vec<*libc::c_char> =
+                tmps.iter()
+                    .map(|tmp| tmp.as_ptr() as *libc::c_char)
+                    .collect();
             ptrs.push(ptr::null());
 
             cb(ptrs.as_ptr() as *c_void)
@@ -702,7 +682,7 @@ fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: proc(*c_void) -> T) -> T {
 }
 
 #[cfg(windows)]
-fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: |*mut c_void| -> T) -> T {
+fn with_envp<T>(env: Option<&[(CString, CString)]>, cb: |*mut c_void| -> T) -> T {
     // On win32 we pass an "environment block" which is not a char**, but
     // rather a concatenation of null-terminated k=v\0 sequences, with a final
     // \0 to terminate.
@@ -711,7 +691,9 @@ fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: |*mut c_void| -> T) -> T {
             let mut blk = Vec::new();
 
             for pair in env.iter() {
-                let kv = format!("{}={}", *pair.ref0(), *pair.ref1());
+                let kv = format!("{}={}",
+                                 pair.ref0().as_str().unwrap(),
+                                 pair.ref1().as_str().unwrap());
                 blk.push_all(kv.to_utf16().as_slice());
                 blk.push(0);
             }
@@ -725,11 +707,12 @@ fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: |*mut c_void| -> T) -> T {
 }
 
 #[cfg(windows)]
-fn with_dirp<T>(d: Option<&Path>, cb: |*u16| -> T) -> T {
+fn with_dirp<T>(d: Option<&CString>, cb: |*u16| -> T) -> T {
     match d {
-      Some(dir) => match dir.as_str() {
-          Some(dir_str) => os::win32::as_utf16_p(dir_str, cb),
-          None => cb(ptr::null())
+      Some(dir) => {
+          let dir_str = dir.as_str()
+                           .expect("expected workingdirectory to be utf-8 encoded");
+          os::win32::as_utf16_p(dir_str, cb)
       },
       None => cb(ptr::null())
     }
@@ -1106,25 +1089,37 @@ mod tests {
 
     #[test] #[cfg(windows)]
     fn test_make_command_line() {
+        use std::str;
+        use std::c_str::CString;
         use super::make_command_line;
+
+        fn test_wrapper(prog: &str, args: &[&str]) -> ~str {
+            make_command_line(&prog.to_c_str(),
+                              args.iter()
+                                  .map(|a| a.to_c_str())
+                                  .collect::<Vec<CString>>()
+                                  .as_slice())
+        }
+
         assert_eq!(
-            make_command_line("prog", ["aaa".to_owned(), "bbb".to_owned(), "ccc".to_owned()]),
+            test_wrapper("prog", ["aaa", "bbb", "ccc"]),
             "prog aaa bbb ccc".to_owned()
         );
+
         assert_eq!(
-            make_command_line("C:\\Program Files\\blah\\blah.exe", ["aaa".to_owned()]),
+            test_wrapper("C:\\Program Files\\blah\\blah.exe", ["aaa"]),
             "\"C:\\Program Files\\blah\\blah.exe\" aaa".to_owned()
         );
         assert_eq!(
-            make_command_line("C:\\Program Files\\test", ["aa\"bb".to_owned()]),
+            test_wrapper("C:\\Program Files\\test", ["aa\"bb"]),
             "\"C:\\Program Files\\test\" aa\\\"bb".to_owned()
         );
         assert_eq!(
-            make_command_line("echo", ["a b c".to_owned()]),
+            test_wrapper("echo", ["a b c"]),
             "echo \"a b c\"".to_owned()
         );
         assert_eq!(
-            make_command_line("\u03c0\u042f\u97f3\u00e6\u221e", []),
+            test_wrapper("\u03c0\u042f\u97f3\u00e6\u221e", []),
             "\u03c0\u042f\u97f3\u00e6\u221e".to_owned()
         );
     }
index 04ce4f88831f4d0cd823b8d1c924bf5f54a10c51..571959d812a45c5210d2b39c2d153c757cf1db39 100644 (file)
@@ -16,7 +16,7 @@
 use lib::llvm::{ArchiveRef, llvm};
 
 use libc;
-use std::io::process::{ProcessConfig, Process, ProcessOutput};
+use std::io::process::{Command, ProcessOutput};
 use std::io::{fs, TempDir};
 use std::io;
 use std::mem;
@@ -39,26 +39,24 @@ pub struct ArchiveRO {
 fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
           paths: &[&Path]) -> ProcessOutput {
     let ar = get_ar_prog(sess);
+    let mut cmd = Command::new(ar.as_slice());
+
+    cmd.arg(args).args(paths);
+    debug!("{}", cmd);
 
-    let mut args = vec!(args.to_owned());
-    let paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
-    args.extend(paths);
-    debug!("{} {}", ar, args.connect(" "));
     match cwd {
-        Some(p) => { debug!("inside {}", p.display()); }
+        Some(p) => {
+            cmd.cwd(p);
+            debug!("inside {}", p.display());
+        }
         None => {}
     }
-    match Process::configure(ProcessConfig {
-        program: ar.as_slice(),
-        args: args.as_slice(),
-        cwd: cwd.map(|a| &*a),
-        .. ProcessConfig::new()
-    }) {
+
+    match cmd.spawn() {
         Ok(prog) => {
             let o = prog.wait_with_output().unwrap();
             if !o.status.success() {
-                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
-                                 o.status));
+                sess.err(format!("{} failed with: {}", cmd, o.status));
                 sess.note(format!("stdout ---\n{}",
                                   str::from_utf8(o.output.as_slice()).unwrap()));
                 sess.note(format!("stderr ---\n{}",
@@ -68,7 +66,7 @@ fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
             o
         },
         Err(e) => {
-            sess.err(format!("could not exec `{}`: {}", ar, e));
+            sess.err(format!("could not exec `{}`: {}", ar.as_slice(), e));
             sess.abort_if_errors();
             fail!("rustc::back::archive::run_ar() should not reach this point");
         }
index de6b5925edbdec8c647a48c6ff74a5747c3e886b..4d70ecb888fdb6ec34136550b4ed33e6c90804ab 100644 (file)
@@ -29,7 +29,7 @@
 
 use std::c_str::{ToCStr, CString};
 use std::char;
-use std::io::{fs, TempDir, Process};
+use std::io::{fs, TempDir, Command};
 use std::io;
 use std::ptr;
 use std::str;
@@ -103,7 +103,7 @@ pub mod write {
     use syntax::abi;
 
     use std::c_str::ToCStr;
-    use std::io::Process;
+    use std::io::{Command};
     use libc::{c_uint, c_int};
     use std::str;
 
@@ -348,22 +348,18 @@ fn with_codegen(tm: TargetMachineRef, llmod: ModuleRef,
     }
 
     pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
-        let cc = super::get_cc_prog(sess);
-        let assembly = outputs.temp_path(OutputTypeAssembly);
-        let object = outputs.path(OutputTypeObject);
-
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let args = [
-            "-c".to_owned(),
-            "-o".to_owned(), object.as_str().unwrap().to_owned(),
-            assembly.as_str().unwrap().to_owned()];
-
-        debug!("{} '{}'", cc, args.connect("' '"));
-        match Process::output(cc.as_slice(), args) {
+        let pname = super::get_cc_prog(sess);
+        let mut cmd = Command::new(pname.as_slice());
+
+        cmd.arg("-c").arg("-o").arg(outputs.path(OutputTypeObject))
+                               .arg(outputs.temp_path(OutputTypeAssembly));
+        debug!("{}", &cmd);
+
+        match cmd.output() {
             Ok(prog) => {
                 if !prog.status.success() {
-                    sess.err(format!("linking with `{}` failed: {}", cc, prog.status));
-                    sess.note(format!("{} arguments: '{}'", cc, args.connect("' '")));
+                    sess.err(format!("linking with `{}` failed: {}", pname, prog.status));
+                    sess.note(format!("{}", &cmd));
                     let mut note = prog.error.clone();
                     note.push_all(prog.output.as_slice());
                     sess.note(str::from_utf8(note.as_slice()).unwrap().to_owned());
@@ -371,7 +367,7 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
                 }
             },
             Err(e) => {
-                sess.err(format!("could not exec the linker `{}`: {}", cc, e));
+                sess.err(format!("could not exec the linker `{}`: {}", pname, e));
                 sess.abort_if_errors();
             }
         }
@@ -527,6 +523,7 @@ unsafe fn populate_llvm_passes(fpm: lib::llvm::PassManagerRef,
  *    system linkers understand.
  */
 
+// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
 pub fn find_crate_id(attrs: &[ast::Attribute], out_filestem: &str) -> CrateId {
     match attr::find_crateid(attrs) {
         None => from_str(out_filestem).unwrap_or_else(|| {
@@ -547,6 +544,7 @@ pub fn crate_id_hash(crate_id: &CrateId) -> StrBuf {
     truncated_hash_result(&mut s).as_slice().slice_to(8).to_strbuf()
 }
 
+// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
 pub fn build_link_meta(krate: &ast::Crate, out_filestem: &str) -> LinkMeta {
     let r = LinkMeta {
         crateid: find_crate_id(krate.attrs.as_slice(), out_filestem),
@@ -1026,31 +1024,30 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
 fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
                  obj_filename: &Path, out_filename: &Path) {
     let tmpdir = TempDir::new("rustc").expect("needs a temp dir");
+
     // The invocations of cc share some flags across platforms
-    let cc_prog = get_cc_prog(sess);
-    let mut cc_args = sess.targ_cfg.target_strs.cc_args.clone();
-    cc_args.push_all_move(link_args(sess, dylib, tmpdir.path(), trans,
-                                    obj_filename, out_filename));
+    let pname = get_cc_prog(sess);
+    let mut cmd = Command::new(pname.as_slice());
+
+    cmd.args(sess.targ_cfg.target_strs.cc_args.as_slice());
+    link_args(&mut cmd, sess, dylib, tmpdir.path(),
+              trans, obj_filename, out_filename);
+
     if (sess.opts.debugging_opts & config::PRINT_LINK_ARGS) != 0 {
-        println!("{} link args: '{}'", cc_prog, cc_args.connect("' '"));
+        println!("{}", &cmd);
     }
 
     // May have not found libraries in the right formats.
     sess.abort_if_errors();
 
     // Invoke the system linker
-    debug!("{} {}", cc_prog, cc_args.connect(" "));
-    let prog = time(sess.time_passes(), "running linker", (), |()|
-                    Process::output(cc_prog.as_slice(),
-                                    cc_args.iter()
-                                           .map(|x| (*x).to_owned())
-                                           .collect::<Vec<_>>()
-                                           .as_slice()));
+    debug!("{}", &cmd);
+    let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output());
     match prog {
         Ok(prog) => {
             if !prog.status.success() {
-                sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
-                sess.note(format!("{} arguments: '{}'", cc_prog, cc_args.connect("' '")));
+                sess.err(format!("linking with `{}` failed: {}", pname, prog.status));
+                sess.note(format!("{}", &cmd));
                 let mut output = prog.error.clone();
                 output.push_all(prog.output.as_slice());
                 sess.note(str::from_utf8(output.as_slice()).unwrap().to_owned());
@@ -1058,7 +1055,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
             }
         },
         Err(e) => {
-            sess.err(format!("could not exec the linker `{}`: {}", cc_prog, e));
+            sess.err(format!("could not exec the linker `{}`: {}", pname, e));
             sess.abort_if_errors();
         }
     }
@@ -1067,9 +1064,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
     // On OSX, debuggers need this utility to get run to do some munging of
     // the symbols
     if sess.targ_cfg.os == abi::OsMacos && (sess.opts.debuginfo != NoDebugInfo) {
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        match Process::status("dsymutil",
-                                  [out_filename.as_str().unwrap().to_owned()]) {
+        match Command::new("dsymutil").arg(out_filename).status() {
             Ok(..) => {}
             Err(e) => {
                 sess.err(format!("failed to run dsymutil: {}", e));
@@ -1079,25 +1074,20 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
     }
 }
 
-fn link_args(sess: &Session,
+fn link_args(cmd: &mut Command,
+             sess: &Session,
              dylib: bool,
              tmpdir: &Path,
              trans: &CrateTranslation,
              obj_filename: &Path,
-             out_filename: &Path) -> Vec<StrBuf> {
+             out_filename: &Path) {
 
     // The default library location, we need this to find the runtime.
     // The location of crates will be determined as needed.
-    // FIXME (#9639): This needs to handle non-utf8 paths
     let lib_path = sess.target_filesearch().get_lib_path();
-    let stage = ("-L".to_owned() + lib_path.as_str().unwrap()).to_strbuf();
+    cmd.arg("-L").arg(lib_path);
 
-    let mut args = vec!(stage);
-
-    // FIXME (#9639): This needs to handle non-utf8 paths
-    args.push_all([
-        "-o".to_strbuf(), out_filename.as_str().unwrap().to_strbuf(),
-        obj_filename.as_str().unwrap().to_strbuf()]);
+    cmd.arg("-o").arg(out_filename).arg(obj_filename);
 
     // Stack growth requires statically linking a __morestack function. Note
     // that this is listed *before* all other libraries, even though it may be
@@ -1114,14 +1104,13 @@ fn link_args(sess: &Session,
     // line, but inserting this farther to the left makes the
     // "rust_stack_exhausted" symbol an outstanding undefined symbol, which
     // flags libstd as a required library (or whatever provides the symbol).
-    args.push("-lmorestack".to_strbuf());
+    cmd.arg("-lmorestack");
 
     // When linking a dynamic library, we put the metadata into a section of the
     // executable. This metadata is in a separate object file from the main
     // object file, so we link that in here.
     if dylib {
-        let metadata = obj_filename.with_extension("metadata.o");
-        args.push(metadata.as_str().unwrap().to_strbuf());
+        cmd.arg(obj_filename.with_extension("metadata.o"));
     }
 
     // We want to prevent the compiler from accidentally leaking in any system
@@ -1132,7 +1121,7 @@ fn link_args(sess: &Session,
     //
     // FIXME(#11937) we should invoke the system linker directly
     if sess.targ_cfg.os != abi::OsWin32 {
-        args.push("-nodefaultlibs".to_strbuf());
+        cmd.arg("-nodefaultlibs");
     }
 
     // If we're building a dylib, we don't use --gc-sections because LLVM has
@@ -1140,20 +1129,20 @@ fn link_args(sess: &Session,
     // metadata. If we're building an executable, however, --gc-sections drops
     // the size of hello world from 1.8MB to 597K, a 67% reduction.
     if !dylib && sess.targ_cfg.os != abi::OsMacos {
-        args.push("-Wl,--gc-sections".to_strbuf());
+        cmd.arg("-Wl,--gc-sections");
     }
 
     if sess.targ_cfg.os == abi::OsLinux {
         // GNU-style linkers will use this to omit linking to libraries which
         // don't actually fulfill any relocations, but only for libraries which
         // follow this flag. Thus, use it before specifying libraries to link to.
-        args.push("-Wl,--as-needed".to_strbuf());
+        cmd.arg("-Wl,--as-needed");
 
         // GNU-style linkers support optimization with -O. GNU ld doesn't need a
         // numeric argument, but other linkers do.
         if sess.opts.optimize == config::Default ||
            sess.opts.optimize == config::Aggressive {
-            args.push("-Wl,-O1".to_strbuf());
+            cmd.arg("-Wl,-O1");
         }
     } else if sess.targ_cfg.os == abi::OsMacos {
         // The dead_strip option to the linker specifies that functions and data
@@ -1166,14 +1155,14 @@ fn link_args(sess: &Session,
         // won't get much benefit from dylibs because LLVM will have already
         // stripped away as much as it could. This has not been seen to impact
         // link times negatively.
-        args.push("-Wl,-dead_strip".to_strbuf());
+        cmd.arg("-Wl,-dead_strip");
     }
 
     if sess.targ_cfg.os == abi::OsWin32 {
         // Make sure that we link to the dynamic libgcc, otherwise cross-module
         // DWARF stack unwinding will not work.
         // This behavior may be overridden by --link-args "-static-libgcc"
-        args.push("-shared-libgcc".to_strbuf());
+        cmd.arg("-shared-libgcc");
 
         // And here, we see obscure linker flags #45. On windows, it has been
         // found to be necessary to have this flag to compile liblibc.
@@ -1200,13 +1189,13 @@ fn link_args(sess: &Session,
         //
         // [1] - https://sourceware.org/bugzilla/show_bug.cgi?id=13130
         // [2] - https://code.google.com/p/go/issues/detail?id=2139
-        args.push("-Wl,--enable-long-section-names".to_strbuf());
+        cmd.arg("-Wl,--enable-long-section-names");
     }
 
     if sess.targ_cfg.os == abi::OsAndroid {
         // Many of the symbols defined in compiler-rt are also defined in libgcc.
         // Android linker doesn't like that by default.
-        args.push("-Wl,--allow-multiple-definition".to_strbuf());
+        cmd.arg("-Wl,--allow-multiple-definition");
     }
 
     // Take careful note of the ordering of the arguments we pass to the linker
@@ -1242,39 +1231,38 @@ fn link_args(sess: &Session,
     // this kind of behavior is pretty platform specific and generally not
     // recommended anyway, so I don't think we're shooting ourself in the foot
     // much with that.
-    add_upstream_rust_crates(&mut args, sess, dylib, tmpdir, trans);
-    add_local_native_libraries(&mut args, sess);
-    add_upstream_native_libraries(&mut args, sess);
+    add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans);
+    add_local_native_libraries(cmd, sess);
+    add_upstream_native_libraries(cmd, sess);
 
     // # Telling the linker what we're doing
 
     if dylib {
         // On mac we need to tell the linker to let this library be rpathed
         if sess.targ_cfg.os == abi::OsMacos {
-            args.push("-dynamiclib".to_strbuf());
-            args.push("-Wl,-dylib".to_strbuf());
-            // FIXME (#9639): This needs to handle non-utf8 paths
+            cmd.args(["-dynamiclib", "-Wl,-dylib"]);
+
             if !sess.opts.cg.no_rpath {
-                args.push(format_strbuf!("-Wl,-install_name,@rpath/{}",
-                                         out_filename.filename_str()
-                                                     .unwrap()));
+                let mut v = Vec::from_slice("-Wl,-install_name,@rpath/".as_bytes());
+                v.push_all(out_filename.filename().unwrap());
+                cmd.arg(v.as_slice());
             }
         } else {
-            args.push("-shared".to_strbuf())
+            cmd.arg("-shared");
         }
     }
 
     if sess.targ_cfg.os == abi::OsFreebsd {
-        args.push_all(["-L/usr/local/lib".to_strbuf(),
-                       "-L/usr/local/lib/gcc46".to_strbuf(),
-                       "-L/usr/local/lib/gcc44".to_strbuf()]);
+        cmd.args(["-L/usr/local/lib",
+                  "-L/usr/local/lib/gcc46",
+                  "-L/usr/local/lib/gcc44"]);
     }
 
     // FIXME (#2397): At some point we want to rpath our guesses as to
     // where extern libraries might live, based on the
     // addl_lib_search_paths
     if !sess.opts.cg.no_rpath {
-        args.push_all(rpath::get_rpath_flags(sess, out_filename).as_slice());
+        cmd.args(rpath::get_rpath_flags(sess, out_filename).as_slice());
     }
 
     // compiler-rt contains implementations of low-level LLVM helpers. This is
@@ -1284,15 +1272,14 @@ fn link_args(sess: &Session,
     //
     // This is the end of the command line, so this library is used to resolve
     // *all* undefined symbols in all other libraries, and this is intentional.
-    args.push("-lcompiler-rt".to_strbuf());
+    cmd.arg("-lcompiler-rt");
 
     // Finally add all the linker arguments provided on the command line along
     // with any #[link_args] attributes found inside the crate
-    args.push_all(sess.opts.cg.link_args.as_slice());
+    cmd.args(sess.opts.cg.link_args.as_slice());
     for arg in sess.cstore.get_used_link_args().borrow().iter() {
-        args.push(arg.clone());
+        cmd.arg(arg.as_slice());
     }
-    return args;
 }
 
 // # Native library linking
@@ -1306,16 +1293,14 @@ fn link_args(sess: &Session,
 // Also note that the native libraries linked here are only the ones located
 // in the current crate. Upstream crates with native library dependencies
 // may have their native library pulled in above.
-fn add_local_native_libraries(args: &mut Vec<StrBuf>, sess: &Session) {
+fn add_local_native_libraries(cmd: &mut Command, sess: &Session) {
     for path in sess.opts.addl_lib_search_paths.borrow().iter() {
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        args.push(("-L" + path.as_str().unwrap().to_owned()).to_strbuf());
+        cmd.arg("-L").arg(path);
     }
 
     let rustpath = filesearch::rust_path();
     for path in rustpath.iter() {
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        args.push(("-L" + path.as_str().unwrap().to_owned()).to_strbuf());
+        cmd.arg("-L").arg(path);
     }
 
     // Some platforms take hints about whether a library is static or dynamic.
@@ -1329,21 +1314,21 @@ fn add_local_native_libraries(args: &mut Vec<StrBuf>, sess: &Session) {
             cstore::NativeUnknown | cstore::NativeStatic => {
                 if takes_hints {
                     if kind == cstore::NativeStatic {
-                        args.push("-Wl,-Bstatic".to_strbuf());
+                        cmd.arg("-Wl,-Bstatic");
                     } else {
-                        args.push("-Wl,-Bdynamic".to_strbuf());
+                        cmd.arg("-Wl,-Bdynamic");
                     }
                 }
-                args.push(format_strbuf!("-l{}", *l));
+                cmd.arg(format_strbuf!("-l{}", *l));
             }
             cstore::NativeFramework => {
-                args.push("-framework".to_strbuf());
-                args.push(l.to_strbuf());
+                cmd.arg("-framework");
+                cmd.arg(l.as_slice());
             }
         }
     }
     if takes_hints {
-        args.push("-Wl,-Bdynamic".to_strbuf());
+        cmd.arg("-Wl,-Bdynamic");
     }
 }
 
@@ -1352,7 +1337,7 @@ fn add_local_native_libraries(args: &mut Vec<StrBuf>, sess: &Session) {
 // Rust crates are not considered at all when creating an rlib output. All
 // dependencies will be linked when producing the final output (instead of
 // the intermediate rlib version)
-fn add_upstream_rust_crates(args: &mut Vec<StrBuf>, sess: &Session,
+fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session,
                             dylib: bool, tmpdir: &Path,
                             trans: &CrateTranslation) {
     // All of the heavy lifting has previously been accomplished by the
@@ -1384,26 +1369,26 @@ fn add_upstream_rust_crates(args: &mut Vec<StrBuf>, sess: &Session,
         let src = sess.cstore.get_used_crate_source(cnum).unwrap();
         match kind {
             cstore::RequireDynamic => {
-                add_dynamic_crate(args, sess, src.dylib.unwrap())
+                add_dynamic_crate(cmd, sess, src.dylib.unwrap())
             }
             cstore::RequireStatic => {
-                add_static_crate(args, sess, tmpdir, cnum, src.rlib.unwrap())
+                add_static_crate(cmd, sess, tmpdir, cnum, src.rlib.unwrap())
             }
         }
 
     }
 
     // Converts a library file-stem into a cc -l argument
-    fn unlib(config: &config::Config, stem: &str) -> StrBuf {
-        if stem.starts_with("lib") && config.os != abi::OsWin32 {
-            stem.slice(3, stem.len()).to_strbuf()
+    fn unlib<'a>(config: &config::Config, stem: &'a [u8]) -> &'a [u8] {
+        if stem.starts_with("lib".as_bytes()) && config.os != abi::OsWin32 {
+            stem.tailn(3)
         } else {
-            stem.to_strbuf()
+            stem
         }
     }
 
     // Adds the static "rlib" versions of all crates to the command line.
-    fn add_static_crate(args: &mut Vec<StrBuf>, sess: &Session, tmpdir: &Path,
+    fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path,
                         cnum: ast::CrateNum, cratepath: Path) {
         // When performing LTO on an executable output, all of the
         // bytecode from the upstream libraries has already been
@@ -1434,34 +1419,32 @@ fn add_static_crate(args: &mut Vec<StrBuf>, sess: &Session, tmpdir: &Path,
                         sess.abort_if_errors();
                     }
                 }
-                let dst_str = dst.as_str().unwrap().to_strbuf();
-                let mut archive = Archive::open(sess, dst);
+                let mut archive = Archive::open(sess, dst.clone());
                 archive.remove_file(format!("{}.o", name));
                 let files = archive.files();
                 if files.iter().any(|s| s.as_slice().ends_with(".o")) {
-                    args.push(dst_str);
+                    cmd.arg(dst);
                 }
             });
         } else {
-            args.push(cratepath.as_str().unwrap().to_strbuf());
+            cmd.arg(cratepath);
         }
     }
 
     // Same thing as above, but for dynamic crates instead of static crates.
-    fn add_dynamic_crate(args: &mut Vec<StrBuf>, sess: &Session,
-                         cratepath: Path) {
+    fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: Path) {
         // If we're performing LTO, then it should have been previously required
         // that all upstream rust dependencies were available in an rlib format.
         assert!(!sess.lto());
 
         // Just need to tell the linker about where the library lives and
         // what its name is
-        let dir = cratepath.dirname_str().unwrap();
-        if !dir.is_empty() {
-            args.push(format_strbuf!("-L{}", dir));
-        }
-        let libarg = unlib(&sess.targ_cfg, cratepath.filestem_str().unwrap());
-        args.push(format_strbuf!("-l{}", libarg));
+        let dir = cratepath.dirname();
+        if !dir.is_empty() { cmd.arg("-L").arg(dir); }
+
+        let mut v = Vec::from_slice("-l".as_bytes());
+        v.push_all(unlib(&sess.targ_cfg, cratepath.filestem().unwrap()));
+        cmd.arg(v.as_slice());
     }
 }
 
@@ -1470,12 +1453,12 @@ fn add_dynamic_crate(args: &mut Vec<StrBuf>, sess: &Session,
 // dependencies. We've got two cases then:
 //
 // 1. The upstream crate is an rlib. In this case we *must* link in the
-//    native dependency because the rlib is just an archive.
+// native dependency because the rlib is just an archive.
 //
 // 2. The upstream crate is a dylib. In order to use the dylib, we have to
-//    have the dependency present on the system somewhere. Thus, we don't
-//    gain a whole lot from not linking in the dynamic dependency to this
-//    crate as well.
+// have the dependency present on the system somewhere. Thus, we don't
+// gain a whole lot from not linking in the dynamic dependency to this
+// crate as well.
 //
 // The use case for this is a little subtle. In theory the native
 // dependencies of a crate are purely an implementation detail of the crate
@@ -1483,7 +1466,7 @@ fn add_dynamic_crate(args: &mut Vec<StrBuf>, sess: &Session,
 // generic function calls a native function, then the generic function must
 // be instantiated in the target crate, meaning that the native symbol must
 // also be resolved in the target crate.
-fn add_upstream_native_libraries(args: &mut Vec<StrBuf>, sess: &Session) {
+fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) {
     // Be sure to use a topological sorting of crates because there may be
     // interdependencies between native libraries. When passing -nodefaultlibs,
     // for example, almost all native libraries depend on libc, so we have to
@@ -1499,11 +1482,11 @@ fn add_upstream_native_libraries(args: &mut Vec<StrBuf>, sess: &Session) {
         for &(kind, ref lib) in libs.iter() {
             match kind {
                 cstore::NativeUnknown => {
-                    args.push(format_strbuf!("-l{}", *lib))
+                    cmd.arg(format_strbuf!("-l{}", *lib));
                 }
                 cstore::NativeFramework => {
-                    args.push("-framework".to_strbuf());
-                    args.push(lib.to_strbuf());
+                    cmd.arg("-framework");
+                    cmd.arg(lib.as_slice());
                 }
                 cstore::NativeStatic => {
                     sess.bug("statics shouldn't be propagated");
index 9e63848b90eab0d9d2f821e630229e1654513402..024f5a1b3fda7bdf56cdcab064165ab5ded429cc 100644 (file)
@@ -11,7 +11,7 @@
 use std::cell::RefCell;
 use std::char;
 use std::io;
-use std::io::{Process, TempDir};
+use std::io::{Command, TempDir};
 use std::os;
 use std::str;
 use std::strbuf::StrBuf;
@@ -155,9 +155,7 @@ fn runtest(test: &str, cratename: &str, libs: HashSet<Path>, should_fail: bool,
     if no_run { return }
 
     // Run the code!
-    let exe = outdir.path().join("rust_out");
-    let out = Process::output(exe.as_str().unwrap(), []);
-    match out {
+    match Command::new(outdir.path().join("rust_out")).output() {
         Err(e) => fail!("couldn't run the test: {}{}", e,
                         if e.kind == io::PermissionDenied {
                             " - maybe your tempdir is mounted with noexec?"
index 7afac6801519b652fe9453886c004164ced706f9..f6fcf3e48162fa138e251dc4cdb0d6e62862f6bc 100644 (file)
@@ -13,7 +13,8 @@
 use std::io::IoError;
 use std::io::process;
 use std::ptr;
-use std::rt::rtio::RtioProcess;
+use std::c_str::CString;
+use std::rt::rtio::{ProcessConfig, RtioProcess};
 use std::rt::task::BlockedTask;
 
 use homing::{HomingIO, HomeHandle};
@@ -50,12 +51,10 @@ impl Process {
     ///
     /// Returns either the corresponding process object or an error which
     /// occurred.
-    pub fn spawn(io_loop: &mut UvIoFactory, config: process::ProcessConfig)
-                -> Result<(Box<Process>, Vec<Option<PipeWatcher>>), UvError>
-    {
-        let cwd = config.cwd.map(|s| s.to_c_str());
-        let mut io = vec![config.stdin, config.stdout, config.stderr];
-        for slot in config.extra_io.iter() {
+    pub fn spawn(io_loop: &mut UvIoFactory, cfg: ProcessConfig)
+                -> Result<(Box<Process>, Vec<Option<PipeWatcher>>), UvError> {
+        let mut io = vec![cfg.stdin, cfg.stdout, cfg.stderr];
+        for slot in cfg.extra_io.iter() {
             io.push(*slot);
         }
         let mut stdio = Vec::<uvll::uv_stdio_container_t>::with_capacity(io.len());
@@ -69,16 +68,16 @@ pub fn spawn(io_loop: &mut UvIoFactory, config: process::ProcessConfig)
             }
         }
 
-        let ret = with_argv(config.program, config.args, |argv| {
-            with_env(config.env, |envp| {
+        let ret = with_argv(cfg.program, cfg.args, |argv| {
+            with_env(cfg.env, |envp| {
                 let mut flags = 0;
-                if config.uid.is_some() {
+                if cfg.uid.is_some() {
                     flags |= uvll::PROCESS_SETUID;
                 }
-                if config.gid.is_some() {
+                if cfg.gid.is_some() {
                     flags |= uvll::PROCESS_SETGID;
                 }
-                if config.detach {
+                if cfg.detach {
                     flags |= uvll::PROCESS_DETACHED;
                 }
                 let options = uvll::uv_process_options_t {
@@ -86,15 +85,15 @@ pub fn spawn(io_loop: &mut UvIoFactory, config: process::ProcessConfig)
                     file: unsafe { *argv },
                     args: argv,
                     env: envp,
-                    cwd: match cwd {
-                        Some(ref cwd) => cwd.with_ref(|p| p),
+                    cwd: match cfg.cwd {
+                        Some(cwd) => cwd.with_ref(|p| p),
                         None => ptr::null(),
                     },
                     flags: flags as libc::c_uint,
                     stdio_count: stdio.len() as libc::c_int,
                     stdio: stdio.as_ptr(),
-                    uid: config.uid.unwrap_or(0) as uvll::uv_uid_t,
-                    gid: config.gid.unwrap_or(0) as uvll::uv_gid_t,
+                    uid: cfg.uid.unwrap_or(0) as uvll::uv_uid_t,
+                    gid: cfg.gid.unwrap_or(0) as uvll::uv_gid_t,
                 };
 
                 let handle = UvHandle::alloc(None::<Process>, uvll::UV_PROCESS);
@@ -175,42 +174,53 @@ unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t,
     }
 }
 
-/// Converts the program and arguments to the argv array expected by libuv
-fn with_argv<T>(prog: &str, args: &[~str], f: |**libc::c_char| -> T) -> T {
-    // First, allocation space to put all the C-strings (we need to have
-    // ownership of them somewhere
-    let mut c_strs = Vec::with_capacity(args.len() + 1);
-    c_strs.push(prog.to_c_str());
-    for arg in args.iter() {
-        c_strs.push(arg.to_c_str());
-    }
+/// Converts the program and arguments to the argv array expected by libuv.
+fn with_argv<T>(prog: &CString, args: &[CString], cb: |**libc::c_char| -> T) -> T {
+    let mut ptrs: Vec<*libc::c_char> = Vec::with_capacity(args.len()+1);
 
-    // Next, create the char** array
-    let mut c_args = Vec::with_capacity(c_strs.len() + 1);
-    for s in c_strs.iter() {
-        c_args.push(s.with_ref(|p| p));
-    }
-    c_args.push(ptr::null());
-    f(c_args.as_ptr())
+    // Convert the CStrings into an array of pointers. Note: the
+    // lifetime of the various CStrings involved is guaranteed to be
+    // larger than the lifetime of our invocation of cb, but this is
+    // technically unsafe as the callback could leak these pointers
+    // out of our scope.
+    ptrs.push(prog.with_ref(|buf| buf));
+    ptrs.extend(args.iter().map(|tmp| tmp.with_ref(|buf| buf)));
+
+    // Add a terminating null pointer (required by libc).
+    ptrs.push(ptr::null());
+
+    cb(ptrs.as_ptr())
 }
 
 /// Converts the environment to the env array expected by libuv
-fn with_env<T>(env: Option<&[(~str, ~str)]>, f: |**libc::c_char| -> T) -> T {
-    let env = match env {
-        Some(s) => s,
-        None => { return f(ptr::null()); }
-    };
-    // As with argv, create some temporary storage and then the actual array
-    let mut envp = Vec::with_capacity(env.len());
-    for &(ref key, ref value) in env.iter() {
-        envp.push(format!("{}={}", *key, *value).to_c_str());
-    }
-    let mut c_envp = Vec::with_capacity(envp.len() + 1);
-    for s in envp.iter() {
-        c_envp.push(s.with_ref(|p| p));
+fn with_env<T>(env: Option<&[(CString, CString)]>, cb: |**libc::c_char| -> T) -> T {
+    // We can pass a char** for envp, which is a null-terminated array
+    // of "k=v\0" strings. Since we must create these strings locally,
+    // yet expose a raw pointer to them, we create a temporary vector
+    // to own the CStrings that outlives the call to cb.
+    match env {
+        Some(env) => {
+            let mut tmps = Vec::with_capacity(env.len());
+
+            for pair in env.iter() {
+                let mut kv = Vec::new();
+                kv.push_all(pair.ref0().as_bytes_no_nul());
+                kv.push('=' as u8);
+                kv.push_all(pair.ref1().as_bytes()); // includes terminal \0
+                tmps.push(kv);
+            }
+
+            // As with `with_argv`, this is unsafe, since cb could leak the pointers.
+            let mut ptrs: Vec<*libc::c_char> =
+                tmps.iter()
+                    .map(|tmp| tmp.as_ptr() as *libc::c_char)
+                    .collect();
+            ptrs.push(ptr::null());
+
+            cb(ptrs.as_ptr())
+        }
+        _ => cb(ptr::null())
     }
-    c_envp.push(ptr::null());
-    f(c_envp.as_ptr())
 }
 
 impl HomingIO for Process {
index c42b17cc3256e92787060891e4a1792f02c51146..1b8175448fc90be62a38d98b196220745f20073f 100644 (file)
@@ -13,7 +13,6 @@
 use std::c_str::CString;
 use std::io::IoError;
 use std::io::net::ip::SocketAddr;
-use std::io::process::ProcessConfig;
 use std::io::signal::Signum;
 use std::io::{FileMode, FileAccess, Open, Append, Truncate, Read, Write,
               ReadWrite, FileStat};
@@ -25,7 +24,7 @@
 use libc;
 use std::path::Path;
 use std::rt::rtio;
-use std::rt::rtio::{IoFactory, EventLoop};
+use std::rt::rtio::{ProcessConfig, IoFactory, EventLoop};
 use ai = std::io::net::addrinfo;
 
 #[cfg(test)] use std::unstable::run_in_bare_thread;
@@ -270,12 +269,12 @@ fn fs_utime(&mut self, path: &CString, atime: u64, mtime: u64)
         r.map_err(uv_error_to_io_error)
     }
 
-    fn spawn(&mut self, config: ProcessConfig)
+    fn spawn(&mut self, cfg: ProcessConfig)
             -> Result<(Box<rtio::RtioProcess:Send>,
                        Vec<Option<Box<rtio::RtioPipe:Send>>>),
                       IoError>
     {
-        match Process::spawn(self, config) {
+        match Process::spawn(self, cfg) {
             Ok((p, io)) => {
                 Ok((p as Box<rtio::RtioProcess:Send>,
                     io.move_iter().map(|i| i.map(|p| {
index 2ee3ee597313bb063806d9b37d8c68704b6add26..5c9d5feab10360230f03497f8f41a85caba4e238 100644 (file)
@@ -245,7 +245,7 @@ fn file_product(p: &Path) -> IoResult<u32> {
 pub use self::net::tcp::TcpStream;
 pub use self::net::udp::UdpStream;
 pub use self::pipe::PipeStream;
-pub use self::process::{Process, ProcessConfig};
+pub use self::process::{Process, Command};
 pub use self::tempfile::TempDir;
 
 pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter};
index 349cac723ff5b3303d487accbcb7283c6fd14599..fe51615285a0081d9e622644501ccf08b391117a 100644 (file)
 
 use prelude::*;
 
+use std::str;
 use fmt;
 use io::IoResult;
 use io;
 use libc;
 use mem;
 use owned::Box;
-use rt::rtio::{RtioProcess, IoFactory, LocalIo};
+use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
+use c_str::CString;
 
 /// Signal a process to exit, without forcibly killing it. Corresponds to
 /// SIGTERM on unix platforms.
 
 /// Representation of a running or exited child process.
 ///
-/// This structure is used to create, run, and manage child processes. A process
-/// is configured with the `ProcessConfig` struct which contains specific
-/// options for dictating how the child is spawned.
+/// This structure is used to represent and manage child processes. A child
+/// process is created via the `Command` struct, which configures the spawning
+/// process and can itself be constructed using a builder-style interface.
 ///
 /// # Example
 ///
 /// ```should_fail
-/// use std::io::Process;
+/// use std::io::Command;
 ///
-/// let mut child = match Process::new("/bin/cat", ["file.txt".to_owned()]) {
+/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() {
 ///     Ok(child) => child,
 ///     Err(e) => fail!("failed to execute child: {}", e),
 /// };
@@ -74,71 +76,244 @@ pub struct Process {
     pub extra_io: Vec<Option<io::PipeStream>>,
 }
 
-/// This configuration describes how a new process should be spawned. A blank
-/// configuration can be created with `ProcessConfig::new()`. It is also
-/// recommented to use a functional struct update pattern when creating process
-/// configuration:
+/// The `Command` type acts as a process builder, providing fine-grained control
+/// over how a new process should be spawned. A default configuration can be
+/// generated using `Command::new(program)`, where `program` gives a path to the
+/// program to be executed. Additional builder methods allow the configuration
+/// to be changed (for example, by adding arguments) prior to spawning:
 ///
 /// ```
-/// use std::io::ProcessConfig;
+/// use std::io::Command;
 ///
-/// let config = ProcessConfig {
-///     program: "/bin/sh",
-///     args: &["-c".to_owned(), "true".to_owned()],
-///     .. ProcessConfig::new()
+/// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() {
+///   Ok(p) => p,
+///   Err(e) => fail!("failed to execute process: {}", e),
 /// };
+///
+/// let output = process.stdout.get_mut_ref().read_to_end();
 /// ```
-pub struct ProcessConfig<'a> {
-    /// Path to the program to run
-    pub program: &'a str,
+pub struct Command {
+    // The internal data for the builder. Documented by the builder
+    // methods below, and serialized into rt::rtio::ProcessConfig.
+    program: CString,
+    args: Vec<CString>,
+    env: Option<Vec<(CString, CString)>>,
+    cwd: Option<CString>,
+    stdin: StdioContainer,
+    stdout: StdioContainer,
+    stderr: StdioContainer,
+    extra_io: Vec<StdioContainer>,
+    uid: Option<uint>,
+    gid: Option<uint>,
+    detach: bool,
+}
+
+// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
+// we cannot usefully take ToCStr arguments by reference (without forcing an
+// additional & around &str). So we are instead temporarily adding an instance
+// for &Path, so that we can take ToCStr as owned. When DST lands, the &Path
+// instance should be removed, and arguments bound by ToCStr should be passed by
+// reference. (Here: {new, arg, args, env}.)
+
+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
+    /// * A readable pipe for stdin (file descriptor 0)
+    /// * A writeable pipe for stdour and stderr (file descriptors 1 and 2)
+    ///
+    /// Builder methods are provided to change these defaults and
+    /// otherwise configure the process.
+    pub fn new<T:ToCStr>(program: T) -> Command {
+        Command {
+            program: program.to_c_str(),
+            args: Vec::new(),
+            env: None,
+            cwd: None,
+            stdin: CreatePipe(true, false),
+            stdout: CreatePipe(false, true),
+            stderr: CreatePipe(false, true),
+            extra_io: Vec::new(),
+            uid: None,
+            gid: None,
+            detach: false,
+        }
+    }
+
+    /// Add an argument to pass to the program.
+    pub fn arg<'a, T:ToCStr>(&'a mut self, arg: T) -> &'a mut Command {
+        self.args.push(arg.to_c_str());
+        self
+    }
 
-    /// Arguments to pass to the program (doesn't include the program itself)
-    pub args: &'a [~str],
+    /// Add multiple arguments to pass to the program.
+    pub fn args<'a, T:ToCStr>(&'a mut self, args: &[T]) -> &'a mut Command {
+        self.args.extend(args.iter().map(|arg| arg.to_c_str()));;
+        self
+    }
 
-    /// Optional environment to specify for the program. If this is None, then
-    /// it will inherit the current process's environment.
-    pub env: Option<&'a [(~str, ~str)]>,
+    /// Sets the environment for the child process (rather than inheriting it
+    /// from the current process).
+
+    // FIXME (#13851): We should change this interface to allow clients to (1)
+    // build up the env vector incrementally and (2) allow both inheriting the
+    // current process's environment AND overriding/adding additional
+    // environment variables. The underlying syscalls assume that the
+    // environment has no duplicate names, so we really want to use a hashtable
+    // to compute the environment to pass down to the syscall; resolving issue
+    // #13851 will make it possible to use the standard hashtable.
+    pub fn env<'a, T:ToCStr>(&'a mut self, env: &[(T,T)]) -> &'a mut Command {
+        self.env = Some(env.iter().map(|&(ref name, ref val)| {
+            (name.to_c_str(), val.to_c_str())
+        }).collect());
+        self
+    }
 
-    /// Optional working directory for the new process. If this is None, then
-    /// the current directory of the running process is inherited.
-    pub cwd: Option<&'a Path>,
+    /// Set the working directory for the child process.
+    pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command {
+        self.cwd = Some(dir.to_c_str());
+        self
+    }
 
     /// Configuration for the child process's stdin handle (file descriptor 0).
-    /// This field defaults to `CreatePipe(true, false)` so the input can be
-    /// written to.
-    pub stdin: StdioContainer,
+    /// Defaults to `CreatePipe(true, false)` so the input can be written to.
+    pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.stdin = cfg;
+        self
+    }
 
     /// Configuration for the child process's stdout handle (file descriptor 1).
-    /// This field defaults to `CreatePipe(false, true)` so the output can be
-    /// collected.
-    pub stdout: StdioContainer,
-
-    /// Configuration for the child process's stdout handle (file descriptor 2).
-    /// This field defaults to `CreatePipe(false, true)` so the output can be
-    /// collected.
-    pub stderr: StdioContainer,
-
-    /// Any number of streams/file descriptors/pipes may be attached to this
-    /// process. This list enumerates the file descriptors and such for the
-    /// process to be spawned, and the file descriptors inherited will start at
-    /// 3 and go to the length of this array. The first three file descriptors
-    /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and
-    /// `stderr` fields.
-    pub extra_io: &'a [StdioContainer],
+    /// Defaults to `CreatePipe(false, true)` so the output can be collected.
+    pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.stdout = cfg;
+        self
+    }
+
+    /// Configuration for the child process's stderr handle (file descriptor 2).
+    /// Defaults to `CreatePipe(false, true)` so the output can be collected.
+    pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.stderr = cfg;
+        self
+    }
+    /// Attaches a stream/file descriptor/pipe to the child process. Inherited
+    /// file descriptors are numbered consecutively, starting at 3; the first
+    /// three file descriptors (stdin/stdout/stderr) are configured with the
+    /// `stdin`, `stdout`, and `stderr` methods.
+    pub fn extra_io<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.extra_io.push(cfg);
+        self
+    }
 
     /// Sets the child process's user id. This translates to a `setuid` call in
     /// the child process. Setting this value on windows will cause the spawn to
     /// fail. Failure in the `setuid` call on unix will also cause the spawn to
     /// fail.
-    pub uid: Option<uint>,
+    pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command {
+        self.uid = Some(id);
+        self
+    }
 
     /// Similar to `uid`, but sets the group id of the child process. This has
     /// the same semantics as the `uid` field.
-    pub gid: Option<uint>,
+    pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command {
+        self.gid = Some(id);
+        self
+    }
 
-    /// If true, the child process is spawned in a detached state. On unix, this
+    /// Sets the child process to be spawned in a detached state. On unix, this
     /// means that the child is the leader of a new process group.
-    pub detach: bool,
+    pub fn detached<'a>(&'a mut self) -> &'a mut Command {
+        self.detach = true;
+        self
+    }
+
+    /// Executes the command as a child process, which is returned.
+    pub fn spawn(&self) -> IoResult<Process> {
+        LocalIo::maybe_raise(|io| {
+            let cfg = ProcessConfig {
+                program: &self.program,
+                args: self.args.as_slice(),
+                env: self.env.as_ref().map(|env| env.as_slice()),
+                cwd: self.cwd.as_ref(),
+                stdin: self.stdin,
+                stdout: self.stdout,
+                stderr: self.stderr,
+                extra_io: self.extra_io.as_slice(),
+                uid: self.uid,
+                gid: self.gid,
+                detach: self.detach,
+            };
+            io.spawn(cfg).map(|(p, io)| {
+                let mut io = io.move_iter().map(|p| {
+                    p.map(|p| io::PipeStream::new(p))
+                });
+                Process {
+                    handle: p,
+                    stdin: io.next().unwrap(),
+                    stdout: io.next().unwrap(),
+                    stderr: io.next().unwrap(),
+                    extra_io: io.collect(),
+                }
+            })
+        })
+    }
+
+    /// Executes the command as a child process, waiting for it to finish and
+    /// collecting all of its output.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use std::io::Command;
+    /// use std::str;
+    ///
+    /// let output = match Command::new("cat").arg("foot.txt").output() {
+    ///     Ok(output) => output,
+    ///     Err(e) => fail!("failed to execute process: {}", e),
+    /// };
+    ///
+    /// println!("status: {}", output.status);
+    /// println!("stdout: {}", str::from_utf8_lossy(output.output.as_slice()));
+    /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice()));
+    /// ```
+    pub fn output(&self) -> IoResult<ProcessOutput> {
+        self.spawn().and_then(|p| p.wait_with_output())
+    }
+
+    /// Executes a command as a child process, waiting for it to finish and
+    /// collecting its exit status.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use std::io::Command;
+    ///
+    /// let status = match Command::new("ls").status() {
+    ///     Ok(status) => status,
+    ///     Err(e) => fail!("failed to execute process: {}", e),
+    /// };
+    ///
+    /// println!("process exited with: {}", status);
+    /// ```
+    pub fn status(&self) -> IoResult<ProcessExit> {
+        self.spawn().and_then(|mut p| p.wait())
+    }
+}
+
+impl fmt::Show for Command {
+    /// Format the program and arguments of a Command for display. Any
+    /// non-utf8 data is lossily converted using the utf8 replacement
+    /// character.
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        try!(write!(f.buf, "{}", str::from_utf8_lossy(self.program.as_bytes_no_nul())));
+        for arg in self.args.iter() {
+            try!(write!(f.buf, " '{}'", str::from_utf8_lossy(arg.as_bytes_no_nul())));
+        }
+        Ok(())
+    }
 }
 
 /// The output of a finished process.
@@ -206,127 +381,7 @@ pub fn matches_exit_status(&self, wanted: int) -> bool {
     }
 }
 
-impl<'a> ProcessConfig<'a> {
-    /// Creates a new configuration with blanks as all of the defaults. This is
-    /// useful when using functional struct updates:
-    ///
-    /// ```rust
-    /// use std::io::process::{ProcessConfig, Process};
-    ///
-    /// let config = ProcessConfig {
-    ///     program: "/bin/sh",
-    ///     args: &["-c".to_owned(), "echo hello".to_owned()],
-    ///     .. ProcessConfig::new()
-    /// };
-    ///
-    /// let p = Process::configure(config);
-    /// ```
-    ///
-    pub fn new<'a>() -> ProcessConfig<'a> {
-        ProcessConfig {
-            program: "",
-            args: &[],
-            env: None,
-            cwd: None,
-            stdin: CreatePipe(true, false),
-            stdout: CreatePipe(false, true),
-            stderr: CreatePipe(false, true),
-            extra_io: &[],
-            uid: None,
-            gid: None,
-            detach: false,
-        }
-    }
-}
-
 impl Process {
-    /// Creates a new process for the specified program/arguments, using
-    /// otherwise default configuration.
-    ///
-    /// By default, new processes have their stdin/stdout/stderr handles created
-    /// as pipes the can be manipulated through the respective fields of the
-    /// returned `Process`.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use std::io::Process;
-    ///
-    /// let mut process = match Process::new("sh", &["c".to_owned(), "echo hello".to_owned()]) {
-    ///     Ok(p) => p,
-    ///     Err(e) => fail!("failed to execute process: {}", e),
-    /// };
-    ///
-    /// let output = process.stdout.get_mut_ref().read_to_end();
-    /// ```
-    pub fn new(prog: &str, args: &[~str]) -> IoResult<Process> {
-        Process::configure(ProcessConfig {
-            program: prog,
-            args: args,
-            .. ProcessConfig::new()
-        })
-    }
-
-    /// Executes the specified program with arguments, waiting for it to finish
-    /// and collecting all of its output.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use std::io::Process;
-    /// use std::str;
-    ///
-    /// let output = match Process::output("cat", ["foo.txt".to_owned()]) {
-    ///     Ok(output) => output,
-    ///     Err(e) => fail!("failed to execute process: {}", e),
-    /// };
-    ///
-    /// println!("status: {}", output.status);
-    /// println!("stdout: {}", str::from_utf8_lossy(output.output.as_slice()));
-    /// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice()));
-    /// ```
-    pub fn output(prog: &str, args: &[~str]) -> IoResult<ProcessOutput> {
-        Process::new(prog, args).and_then(|p| p.wait_with_output())
-    }
-
-    /// Executes a child process and collects its exit status. This will block
-    /// waiting for the child to exit.
-    ///
-    /// # Example
-    ///
-    /// ```
-    /// use std::io::Process;
-    ///
-    /// let status = match Process::status("ls", []) {
-    ///     Ok(status) => status,
-    ///     Err(e) => fail!("failed to execute process: {}", e),
-    /// };
-    ///
-    /// println!("process exited with: {}", status);
-    /// ```
-    pub fn status(prog: &str, args: &[~str]) -> IoResult<ProcessExit> {
-        Process::new(prog, args).and_then(|mut p| p.wait())
-    }
-
-    /// Creates a new process with the specified configuration.
-    pub fn configure(config: ProcessConfig) -> IoResult<Process> {
-        let mut config = Some(config);
-        LocalIo::maybe_raise(|io| {
-            io.spawn(config.take_unwrap()).map(|(p, io)| {
-                let mut io = io.move_iter().map(|p| {
-                    p.map(|p| io::PipeStream::new(p))
-                });
-                Process {
-                    handle: p,
-                    stdin: io.next().unwrap(),
-                    stdout: io.next().unwrap(),
-                    stderr: io.next().unwrap(),
-                    extra_io: io.collect(),
-                }
-            })
-        })
-    }
-
     /// Sends `signal` to another process in the system identified by `id`.
     ///
     /// Note that windows doesn't quite have the same model as unix, so some
@@ -403,11 +458,11 @@ pub fn wait(&mut self) -> IoResult<ProcessExit> {
     ///
     /// ```no_run
     /// # #![allow(experimental)]
-    /// use std::io::process::{Process, ProcessExit};
+    /// use std::io::process::{Command, ProcessExit};
     /// use std::io::IoResult;
     ///
     /// fn run_gracefully(prog: &str) -> IoResult<ProcessExit> {
-    ///     let mut p = try!(Process::new("long-running-process", []));
+    ///     let mut p = try!(Command::new("long-running-process").spawn());
     ///
     ///     // give the process 10 seconds to finish completely
     ///     p.set_timeout(Some(10_000));
@@ -487,18 +542,14 @@ fn drop(&mut self) {
 
 #[cfg(test)]
 mod tests {
-    use io::process::{ProcessConfig, Process};
+    use io::process::{Command, Process};
     use prelude::*;
 
     // FIXME(#10380) these tests should not all be ignored on android.
 
     #[cfg(not(target_os="android"))]
     iotest!(fn smoke() {
-        let args = ProcessConfig {
-            program: "true",
-            .. ProcessConfig::new()
-        };
-        let p = Process::configure(args);
+        let p = Command::new("true").spawn();
         assert!(p.is_ok());
         let mut p = p.unwrap();
         assert!(p.wait().unwrap().success());
@@ -506,11 +557,7 @@ mod tests {
 
     #[cfg(not(target_os="android"))]
     iotest!(fn smoke_failure() {
-        let args = ProcessConfig {
-            program: "if-this-is-a-binary-then-the-world-has-ended",
-            .. ProcessConfig::new()
-        };
-        match Process::configure(args) {
+        match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
             Ok(..) => fail!(),
             Err(..) => {}
         }
@@ -518,11 +565,7 @@ mod tests {
 
     #[cfg(not(target_os="android"))]
     iotest!(fn exit_reported_right() {
-        let args = ProcessConfig {
-            program: "false",
-            .. ProcessConfig::new()
-        };
-        let p = Process::configure(args);
+        let p = Command::new("false").spawn();
         assert!(p.is_ok());
         let mut p = p.unwrap();
         assert!(p.wait().unwrap().matches_exit_status(1));
@@ -531,12 +574,7 @@ mod tests {
 
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn signal_reported_right() {
-        let args = ProcessConfig {
-            program: "/bin/sh",
-            args: &["-c".to_owned(), "kill -1 $$".to_owned()],
-            .. ProcessConfig::new()
-        };
-        let p = Process::configure(args);
+        let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn();
         assert!(p.is_ok());
         let mut p = p.unwrap();
         match p.wait().unwrap() {
@@ -549,8 +587,8 @@ pub fn read_all(input: &mut Reader) -> ~str {
         input.read_to_str().unwrap()
     }
 
-    pub fn run_output(args: ProcessConfig) -> ~str {
-        let p = Process::configure(args);
+    pub fn run_output(cmd: Command) -> ~str {
+        let p = cmd.spawn();
         assert!(p.is_ok());
         let mut p = p.unwrap();
         assert!(p.stdout.is_some());
@@ -561,38 +599,27 @@ pub fn run_output(args: ProcessConfig) -> ~str {
 
     #[cfg(not(target_os="android"))]
     iotest!(fn stdout_works() {
-        let args = ProcessConfig {
-            program: "echo",
-            args: &["foobar".to_owned()],
-            stdout: CreatePipe(false, true),
-            .. ProcessConfig::new()
-        };
-        assert_eq!(run_output(args), "foobar\n".to_owned());
+        let mut cmd = Command::new("echo");
+        cmd.arg("foobar").stdout(CreatePipe(false, true));
+        assert_eq!(run_output(cmd), "foobar\n".to_owned());
     })
 
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn set_cwd_works() {
-        let cwd = Path::new("/");
-        let args = ProcessConfig {
-            program: "/bin/sh",
-            args: &["-c".to_owned(), "pwd".to_owned()],
-            cwd: Some(&cwd),
-            stdout: CreatePipe(false, true),
-            .. ProcessConfig::new()
-        };
-        assert_eq!(run_output(args), "/\n".to_owned());
+        let mut cmd = Command::new("/bin/sh");
+        cmd.arg("-c").arg("pwd")
+           .cwd(&Path::new("/"))
+           .stdout(CreatePipe(false, true));
+        assert_eq!(run_output(cmd), "/\n".to_owned());
     })
 
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn stdin_works() {
-        let args = ProcessConfig {
-            program: "/bin/sh",
-            args: &["-c".to_owned(), "read line; echo $line".to_owned()],
-            stdin: CreatePipe(true, false),
-            stdout: CreatePipe(false, true),
-            .. ProcessConfig::new()
-        };
-        let mut p = Process::configure(args).unwrap();
+        let mut p = Command::new("/bin/sh")
+                            .arg("-c").arg("read line; echo $line")
+                            .stdin(CreatePipe(true, false))
+                            .stdout(CreatePipe(false, true))
+                            .spawn().unwrap();
         p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap();
         drop(p.stdin.take());
         let out = read_all(p.stdout.get_mut_ref() as &mut Reader);
@@ -602,36 +629,23 @@ pub fn run_output(args: ProcessConfig) -> ~str {
 
     #[cfg(not(target_os="android"))]
     iotest!(fn detach_works() {
-        let args = ProcessConfig {
-            program: "true",
-            detach: true,
-            .. ProcessConfig::new()
-        };
-        let mut p = Process::configure(args).unwrap();
+        let mut p = Command::new("true").detached().spawn().unwrap();
         assert!(p.wait().unwrap().success());
     })
 
     #[cfg(windows)]
     iotest!(fn uid_fails_on_windows() {
-        let args = ProcessConfig {
-            program: "test",
-            uid: Some(10),
-            .. ProcessConfig::new()
-        };
-        assert!(Process::configure(args).is_err());
+        assert!(Command::new("test").uid(10).spawn().is_err());
     })
 
     #[cfg(unix, not(target_os="android"))]
     iotest!(fn uid_works() {
         use libc;
-        let args = ProcessConfig {
-            program: "/bin/sh",
-            args: &["-c".to_owned(), "true".to_owned()],
-            uid: Some(unsafe { libc::getuid() as uint }),
-            gid: Some(unsafe { libc::getgid() as uint }),
-            .. ProcessConfig::new()
-        };
-        let mut p = Process::configure(args).unwrap();
+        let mut p = Command::new("/bin/sh")
+                            .arg("-c").arg("true")
+                            .uid(unsafe { libc::getuid() as uint })
+                            .gid(unsafe { libc::getgid() as uint })
+                            .spawn().unwrap();
         assert!(p.wait().unwrap().success());
     })
 
@@ -642,26 +656,20 @@ pub fn run_output(args: ProcessConfig) -> ~str {
         // if we're already root, this isn't a valid test. Most of the bots run
         // as non-root though (android is an exception).
         if unsafe { libc::getuid() == 0 } { return }
-        let args = ProcessConfig {
-            program: "/bin/ls",
-            uid: Some(0),
-            gid: Some(0),
-            .. ProcessConfig::new()
-        };
-        assert!(Process::configure(args).is_err());
+        assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err());
     })
 
     #[cfg(not(target_os="android"))]
     iotest!(fn test_process_status() {
-        let mut status = Process::status("false", []).unwrap();
+        let mut status = Command::new("false").status().unwrap();
         assert!(status.matches_exit_status(1));
 
-        status = Process::status("true", []).unwrap();
+        status = Command::new("true").status().unwrap();
         assert!(status.success());
     })
 
     iotest!(fn test_process_output_fail_to_start() {
-        match Process::output("/no-binary-by-this-name-should-exist", []) {
+        match Command::new("/no-binary-by-this-name-should-exist").output() {
             Err(e) => assert_eq!(e.kind, FileNotFound),
             Ok(..) => fail!()
         }
@@ -669,9 +677,8 @@ pub fn run_output(args: ProcessConfig) -> ~str {
 
     #[cfg(not(target_os="android"))]
     iotest!(fn test_process_output_output() {
-
         let ProcessOutput {status, output, error}
-             = Process::output("echo", ["hello".to_owned()]).unwrap();
+             = Command::new("echo").arg("hello").output().unwrap();
         let output_str = str::from_utf8(output.as_slice()).unwrap();
 
         assert!(status.success());
@@ -685,7 +692,7 @@ pub fn run_output(args: ProcessConfig) -> ~str {
     #[cfg(not(target_os="android"))]
     iotest!(fn test_process_output_error() {
         let ProcessOutput {status, output, error}
-             = Process::output("mkdir", [".".to_owned()]).unwrap();
+             = Command::new("mkdir").arg(".").output().unwrap();
 
         assert!(status.matches_exit_status(1));
         assert_eq!(output, Vec::new());
@@ -694,21 +701,20 @@ pub fn run_output(args: ProcessConfig) -> ~str {
 
     #[cfg(not(target_os="android"))]
     iotest!(fn test_finish_once() {
-        let mut prog = Process::new("false", []).unwrap();
+        let mut prog = Command::new("false").spawn().unwrap();
         assert!(prog.wait().unwrap().matches_exit_status(1));
     })
 
     #[cfg(not(target_os="android"))]
     iotest!(fn test_finish_twice() {
-        let mut prog = Process::new("false", []).unwrap();
+        let mut prog = Command::new("false").spawn().unwrap();
         assert!(prog.wait().unwrap().matches_exit_status(1));
         assert!(prog.wait().unwrap().matches_exit_status(1));
     })
 
     #[cfg(not(target_os="android"))]
     iotest!(fn test_wait_with_output_once() {
-
-        let prog = Process::new("echo", ["hello".to_owned()]).unwrap();
+        let prog = Command::new("echo").arg("hello").spawn().unwrap();
         let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap();
         let output_str = str::from_utf8(output.as_slice()).unwrap();
 
@@ -721,36 +727,26 @@ pub fn run_output(args: ProcessConfig) -> ~str {
     })
 
     #[cfg(unix,not(target_os="android"))]
-    pub fn run_pwd(dir: Option<&Path>) -> Process {
-        Process::configure(ProcessConfig {
-            program: "pwd",
-            cwd: dir,
-            .. ProcessConfig::new()
-        }).unwrap()
+    pub fn pwd_cmd() -> Command {
+        Command::new("pwd")
     }
     #[cfg(target_os="android")]
-    pub fn run_pwd(dir: Option<&Path>) -> Process {
-        Process::configure(ProcessConfig {
-            program: "/system/bin/sh",
-            args: &["-c".to_owned(),"pwd".to_owned()],
-            cwd: dir.map(|a| &*a),
-            .. ProcessConfig::new()
-        }).unwrap()
+    pub fn pwd_cmd() -> Command {
+        let mut cmd = Command::new("/system/bin/sh");
+        cmd.arg("-c").arg("pwd");
+        cmd
     }
 
     #[cfg(windows)]
-    pub fn run_pwd(dir: Option<&Path>) -> Process {
-        Process::configure(ProcessConfig {
-            program: "cmd",
-            args: &["/c".to_owned(), "cd".to_owned()],
-            cwd: dir.map(|a| &*a),
-            .. ProcessConfig::new()
-        }).unwrap()
+    pub fn pwd_cmd() -> Command {
+        let mut cmd = Command::new("cmd");
+        cmd.arg("/c").arg("cd");
+        cmd
     }
 
     iotest!(fn test_keep_current_working_dir() {
         use os;
-        let prog = run_pwd(None);
+        let prog = pwd_cmd().spawn().unwrap();
 
         let output = str::from_utf8(prog.wait_with_output().unwrap()
                                         .output.as_slice()).unwrap().to_owned();
@@ -769,7 +765,7 @@ pub fn run_pwd(dir: Option<&Path>) -> Process {
         // test changing to the parent of os::getcwd() because we know
         // the path exists (and os::getcwd() is not expected to be root)
         let parent_dir = os::getcwd().dir_path();
-        let prog = run_pwd(Some(&parent_dir));
+        let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap();
 
         let output = str::from_utf8(prog.wait_with_output().unwrap()
                                         .output.as_slice()).unwrap().to_owned();
@@ -783,31 +779,21 @@ pub fn run_pwd(dir: Option<&Path>) -> Process {
     })
 
     #[cfg(unix,not(target_os="android"))]
-    pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
-        Process::configure(ProcessConfig {
-            program: "env",
-            env: env.as_ref().map(|e| e.as_slice()),
-            .. ProcessConfig::new()
-        }).unwrap()
+    pub fn env_cmd() -> Command {
+        Command::new("env")
     }
     #[cfg(target_os="android")]
-    pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
-        Process::configure(ProcessConfig {
-            program: "/system/bin/sh",
-            args: &["-c".to_owned(),"set".to_owned()],
-            env: env.as_ref().map(|e| e.as_slice()),
-            .. ProcessConfig::new()
-        }).unwrap()
+    pub fn env_cmd() -> Command {
+        let mut cmd = Command::new("/system/bin/sh");
+        cmd.arg("-c").arg("set");
+        cmd
     }
 
     #[cfg(windows)]
-    pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
-        Process::configure(ProcessConfig {
-            program: "cmd",
-            args: &["/c".to_owned(), "set".to_owned()],
-            env: env.as_ref().map(|e| e.as_slice()),
-            .. ProcessConfig::new()
-        }).unwrap()
+    pub fn env_cmd() -> Command {
+        let mut cmd = Command::new("cmd");
+        cmd.arg("/c").arg("set");
+        cmd
     }
 
     #[cfg(not(target_os="android"))]
@@ -815,7 +801,7 @@ pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
         use os;
         if running_on_valgrind() { return; }
 
-        let prog = run_env(None);
+        let prog = env_cmd().spawn().unwrap();
         let output = str::from_utf8(prog.wait_with_output().unwrap()
                                         .output.as_slice()).unwrap().to_owned();
 
@@ -830,7 +816,7 @@ pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
         use os;
         if running_on_valgrind() { return; }
 
-        let prog = run_env(None);
+        let mut prog = env_cmd().spawn().unwrap();
         let output = str::from_utf8(prog.wait_with_output()
                                         .unwrap().output.as_slice())
                                    .unwrap().to_owned();
@@ -846,9 +832,8 @@ pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
     })
 
     iotest!(fn test_add_to_env() {
-        let new_env = box [("RUN_TEST_NEW_ENV".to_owned(), "123".to_owned())];
-
-        let prog = run_env(Some(new_env));
+        let new_env = box [("RUN_TEST_NEW_ENV", "123")];
+        let prog = env_cmd().env(new_env).spawn().unwrap();
         let result = prog.wait_with_output().unwrap();
         let output = str::from_utf8_lossy(result.output.as_slice()).into_owned();
 
@@ -858,14 +843,14 @@ pub fn run_env(env: Option<~[(~str, ~str)]>) -> Process {
 
     #[cfg(unix)]
     pub fn sleeper() -> Process {
-        Process::new("sleep", ["1000".to_owned()]).unwrap()
+        Command::new("sleep").arg("1000").spawn().unwrap()
     }
     #[cfg(windows)]
     pub fn sleeper() -> Process {
         // There's a `timeout` command on windows, but it doesn't like having
         // its output piped, so instead just ping ourselves a few times with
         // gaps inbetweeen so we're sure this process is alive for awhile
-        Process::new("ping", ["127.0.0.1".to_owned(), "-n".to_owned(), "1000".to_owned()]).unwrap()
+        Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap()
     }
 
     iotest!(fn test_kill() {
index 90f97e59caadad6a4d0960d95fcc2f9d4b2355ee..a6c60df2642362798c899343eddcfa535c5bc8f2 100644 (file)
@@ -29,7 +29,7 @@
 use io;
 use io::IoResult;
 use io::net::ip::{IpAddr, SocketAddr};
-use io::process::{ProcessConfig, ProcessExit};
+use io::process::{StdioContainer, ProcessExit};
 use io::signal::Signum;
 use io::{FileMode, FileAccess, FileStat, FilePermission};
 use io::{SeekStyle};
@@ -87,6 +87,61 @@ pub enum CloseBehavior {
     CloseAsynchronously,
 }
 
+/// Data needed to spawn a process. Serializes the `std::io::process::Command`
+/// builder.
+pub struct ProcessConfig<'a> {
+    /// Path to the program to run.
+    pub program: &'a CString,
+
+    /// Arguments to pass to the program (doesn't include the program itself).
+    pub args: &'a [CString],
+
+    /// Optional environment to specify for the program. If this is None, then
+    /// it will inherit the current process's environment.
+    pub env: Option<&'a [(CString, CString)]>,
+
+    /// Optional working directory for the new process. If this is None, then
+    /// the current directory of the running process is inherited.
+    pub cwd: Option<&'a CString>,
+
+    /// Configuration for the child process's stdin handle (file descriptor 0).
+    /// This field defaults to `CreatePipe(true, false)` so the input can be
+    /// written to.
+    pub stdin: StdioContainer,
+
+    /// Configuration for the child process's stdout handle (file descriptor 1).
+    /// This field defaults to `CreatePipe(false, true)` so the output can be
+    /// collected.
+    pub stdout: StdioContainer,
+
+    /// Configuration for the child process's stdout handle (file descriptor 2).
+    /// This field defaults to `CreatePipe(false, true)` so the output can be
+    /// collected.
+    pub stderr: StdioContainer,
+
+    /// Any number of streams/file descriptors/pipes may be attached to this
+    /// process. This list enumerates the file descriptors and such for the
+    /// process to be spawned, and the file descriptors inherited will start at
+    /// 3 and go to the length of this array. The first three file descriptors
+    /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and
+    /// `stderr` fields.
+    pub extra_io: &'a [StdioContainer],
+
+    /// Sets the child process's user id. This translates to a `setuid` call in
+    /// the child process. Setting this value on windows will cause the spawn to
+    /// fail. Failure in the `setuid` call on unix will also cause the spawn to
+    /// fail.
+    pub uid: Option<uint>,
+
+    /// Similar to `uid`, but sets the group id of the child process. This has
+    /// the same semantics as the `uid` field.
+    pub gid: Option<uint>,
+
+    /// If true, the child process is spawned in a detached state. On unix, this
+    /// means that the child is the leader of a new process group.
+    pub detach: bool,
+}
+
 pub struct LocalIo<'a> {
     factory: &'a mut IoFactory,
 }
@@ -189,7 +244,7 @@ fn fs_utime(&mut self, src: &CString, atime: u64, mtime: u64) ->
 
     // misc
     fn timer_init(&mut self) -> IoResult<Box<RtioTimer:Send>>;
-    fn spawn(&mut self, config: ProcessConfig)
+    fn spawn(&mut self, cfg: ProcessConfig)
             -> IoResult<(Box<RtioProcess:Send>,
                          Vec<Option<Box<RtioPipe:Send>>>)>;
     fn kill(&mut self, pid: libc::pid_t, signal: int) -> IoResult<()>;
index 87d531cc627e093dbc7723749e800086931d2a43..1e88bffd00ea98de50f34d5553d704e20dd84aa2 100644 (file)
@@ -221,8 +221,6 @@ pub enum RTLD {
 pub mod dl {
     use libc;
     use os;
-    use path::GenericPath;
-    use path;
     use ptr;
     use result::{Ok, Err, Result};
 
index c2dd8459540d89724a7d8d4084445f51c188b196..98fbb00f4378d37d8f99806b43d3eb2c2c41df66 100644 (file)
@@ -488,7 +488,7 @@ pub fn unwrap(self) -> T {
 #[cfg(not(target_os="android"))] // FIXME(#10455)
 fn test() {
     use std::os;
-    use std::io::{fs, Process};
+    use std::io::{fs, Command};
     use std::str::from_utf8;
 
     // Create a path to a new file 'filename' in the directory in which
@@ -522,10 +522,7 @@ fn make_path(filename: StrBuf) -> Path {
         prep.exec(proc(_exe) {
             let out = make_path("foo.o".to_strbuf());
             let compiler = if cfg!(windows) {"gcc"} else {"cc"};
-            // FIXME (#9639): This needs to handle non-utf8 paths
-            Process::status(compiler, [pth.as_str().unwrap().to_owned(),
-                                    "-o".to_owned(),
-                                    out.as_str().unwrap().to_owned()]).unwrap();
+            Command::new(compiler).arg(pth).arg("-o").arg(out.clone()).status().unwrap();
 
             let _proof_of_concept = subcx.prep("subfn");
             // Could run sub-rules inside here.
index ab3539ebf6f54773e6f7fd04cbf5e9b75b0dd59b..4ae0b6f14f59d4fcd009a629edf46aa8f0e38b55 100644 (file)
@@ -27,7 +27,8 @@ fn bar() { }
 fn baz() { }
 
 pub fn test() {
-    let lib = DynamicLibrary::open(None).unwrap();
+    let none: Option<Path> = None; // appease the typechecker
+    let lib = DynamicLibrary::open(none).unwrap();
     unsafe {
         assert!(lib.symbol::<int>("foo").is_ok());
         assert!(lib.symbol::<int>("baz").is_err());
index a08d6bb0bf87fdb45ea3c309e80a0be2305225bd..219eb1a3ebd459da0c14e9a4d93d91cead169934 100644 (file)
@@ -12,7 +12,7 @@
 use rand::{task_rng, Rng};
 
 use std::{char, os, str};
-use std::io::{File, Process};
+use std::io::{File, Command};
 
 // creates unicode_input_multiple_files_{main,chars}.rs, where the
 // former imports the latter. `_chars` just contains an indentifier
@@ -40,7 +40,6 @@ fn main() {
     let tmpdir = Path::new(args.get(2).as_slice());
 
     let main_file = tmpdir.join("unicode_input_multiple_files_main.rs");
-    let main_file_str = main_file.as_str().unwrap();
     {
         let _ = File::create(&main_file).unwrap()
             .write_str("mod unicode_input_multiple_files_chars;");
@@ -57,7 +56,9 @@ fn main() {
 
         // rustc is passed to us with --out-dir and -L etc., so we
         // can't exec it directly
-        let result = Process::output("sh", ["-c".to_owned(), rustc + " " + main_file_str]).unwrap();
+        let result = Command::new("sh")
+                             .arg("-c").arg(rustc + " " + main_file.as_str().unwrap())
+                             .output().unwrap();
         let err = str::from_utf8_lossy(result.error.as_slice());
 
         // positive test so that this test will be updated when the
index d56149752155161821870fe6eba580db3339b333..2bb89d7621320c2634c276c184f5d3bebbb22ed7 100644 (file)
@@ -12,7 +12,7 @@
 use rand::{task_rng, Rng};
 
 use std::{char, os, str};
-use std::io::{File, Process};
+use std::io::{File, Command};
 
 // creates a file with `fn main() { <random ident> }` and checks the
 // compiler emits a span of the appropriate length (for the
@@ -37,9 +37,7 @@ fn main() {
     let args = os::args();
     let rustc = args.get(1).as_slice();
     let tmpdir = Path::new(args.get(2).as_slice());
-
     let main_file = tmpdir.join("span_main.rs");
-    let main_file_str = main_file.as_str().unwrap();
 
     for _ in range(0, 100) {
         let n = task_rng().gen_range(3u, 20);
@@ -53,7 +51,9 @@ fn main() {
 
         // rustc is passed to us with --out-dir and -L etc., so we
         // can't exec it directly
-        let result = Process::output("sh", ["-c".to_owned(), rustc + " " + main_file_str]).unwrap();
+        let result = Command::new("sh")
+                             .arg("-c").arg(rustc + " " + main_file.as_str().unwrap())
+                             .output().unwrap();
 
         let err = str::from_utf8_lossy(result.error.as_slice());
 
index 260ce69f821d768847880cc264f7f3d795347e70..25df896ec15a12caff393dde26a9aedb00a746a0 100644 (file)
@@ -14,7 +14,7 @@
 extern crate native;
 
 use std::os;
-use std::io::process::{Process, ProcessConfig};
+use std::io::process::Command;
 use std::unstable::finally::Finally;
 use std::str;
 
@@ -48,15 +48,7 @@ fn runtest(me: &str) {
     env.push(("RUST_BACKTRACE".to_strbuf(), "1".to_strbuf()));
 
     // Make sure that the stack trace is printed
-    let env = env.iter()
-                 .map(|&(ref k, ref v)| (k.to_owned(), v.to_owned()))
-                 .collect::<Vec<_>>();
-    let mut p = Process::configure(ProcessConfig {
-        program: me,
-        args: ["fail".to_owned()],
-        env: Some(env.as_slice()),
-        .. ProcessConfig::new()
-    }).unwrap();
+    let mut p = Command::new(me).arg("fail").env(env.as_slice()).spawn().unwrap();
     let out = p.wait_with_output().unwrap();
     assert!(!out.status.success());
     let s = str::from_utf8(out.error.as_slice()).unwrap();
@@ -64,11 +56,7 @@ fn runtest(me: &str) {
             "bad output: {}", s);
 
     // Make sure the stack trace is *not* printed
-    let mut p = Process::configure(ProcessConfig {
-        program: me,
-        args: ["fail".to_owned()],
-        .. ProcessConfig::new()
-    }).unwrap();
+    let mut p = Command::new(me).arg("fail").spawn().unwrap();
     let out = p.wait_with_output().unwrap();
     assert!(!out.status.success());
     let s = str::from_utf8(out.error.as_slice()).unwrap();
@@ -76,11 +64,7 @@ fn runtest(me: &str) {
             "bad output2: {}", s);
 
     // Make sure a stack trace is printed
-    let mut p = Process::configure(ProcessConfig {
-        program: me,
-        args: ["double-fail".to_owned()],
-        .. ProcessConfig::new()
-    }).unwrap();
+    let mut p = Command::new(me).arg("double-fail").spawn().unwrap();
     let out = p.wait_with_output().unwrap();
     assert!(!out.status.success());
     let s = str::from_utf8(out.error.as_slice()).unwrap();
@@ -88,12 +72,7 @@ fn runtest(me: &str) {
             "bad output3: {}", s);
 
     // Make sure a stack trace isn't printed too many times
-    let mut p = Process::configure(ProcessConfig {
-        program: me,
-        args: ["double-fail".to_owned()],
-        env: Some(env.as_slice()),
-        .. ProcessConfig::new()
-    }).unwrap();
+    let mut p = Command::new(me).arg("double-fail").env(env.as_slice()).spawn().unwrap();
     let out = p.wait_with_output().unwrap();
     assert!(!out.status.success());
     let s = str::from_utf8(out.error.as_slice()).unwrap();
index 01a71d862b4ff9396a89b363277e5947e18ad85c..a0d4785d8d2be420b79ff024251575dfe953815e 100644 (file)
@@ -22,7 +22,7 @@
 extern crate green;
 extern crate rustuv;
 
-use std::io::Process;
+use std::io::{Process, Command};
 
 macro_rules! succeed( ($e:expr) => (
     match $e { Ok(..) => {}, Err(e) => fail!("failure: {}", e) }
@@ -36,7 +36,7 @@ mod $name {
             use std::io::timer;
             use libc;
             use std::str;
-            use std::io::process::{Process, ProcessOutput};
+            use std::io::process::Command;
             use native;
             use super::*;
 
@@ -68,14 +68,14 @@ fn start(argc: int, argv: **u8) -> int {
 
 #[cfg(unix)]
 pub fn sleeper() -> Process {
-    Process::new("sleep", ["1000".to_owned()]).unwrap()
+    Command::new("sleep").arg("1000").spawn().unwrap()
 }
 #[cfg(windows)]
 pub fn sleeper() -> Process {
     // There's a `timeout` command on windows, but it doesn't like having
     // its output piped, so instead just ping ourselves a few times with
     // gaps inbetweeen so we're sure this process is alive for awhile
-    Process::new("ping", ["127.0.0.1".to_owned(), "-n".to_owned(), "1000".to_owned()]).unwrap()
+    Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap()
 }
 
 iotest!(fn test_destroy_twice() {
@@ -85,7 +85,7 @@ pub fn sleeper() -> Process {
 })
 
 pub fn test_destroy_actually_kills(force: bool) {
-    use std::io::process::{Process, ProcessOutput, ExitStatus, ExitSignal};
+    use std::io::process::{Command, ProcessOutput, ExitStatus, ExitSignal};
     use std::io::timer;
     use libc;
     use std::str;
@@ -100,7 +100,7 @@ pub fn test_destroy_actually_kills(force: bool) {
     static BLOCK_COMMAND: &'static str = "cmd";
 
     // this process will stay alive indefinitely trying to read from stdin
-    let mut p = Process::new(BLOCK_COMMAND, []).unwrap();
+    let mut p = Command::new(BLOCK_COMMAND).spawn().unwrap();
 
     assert!(p.signal(0).is_ok());
 
index 38030eb6c1fdcb72cb0a1c70ffb114795958665f..dd513547212666c51ef9303e325051a4e88b42a7 100644 (file)
@@ -18,7 +18,7 @@
 pub fn main () {
     let args = os::args();
     let args = args.as_slice();
-    if args.len() > 1 && args[1] == "child".to_owned() {
+    if args.len() > 1 && args[1].as_slice() == "child" {
         for _ in range(0, 1000) {
             println!("hello?");
         }
@@ -28,14 +28,7 @@ pub fn main () {
         return;
     }
 
-    let config = process::ProcessConfig {
-        program : args[0].as_slice(),
-        args : &["child".to_owned()],
-        stdout: process::Ignored,
-        stderr: process::Ignored,
-        .. process::ProcessConfig::new()
-    };
-
-    let mut p = process::Process::configure(config).unwrap();
-    println!("{}", p.wait());
+    let mut p = process::Command::new(args[0].as_slice());
+    p.arg("child").stdout(process::Ignored).stderr(process::Ignored);
+    println!("{}", p.spawn().unwrap().wait());
 }
index 36db5a64555b59deb0fc03b25b60cf14220cce69..36fc48432a8217693eab78cc7895d34a84c28d0f 100644 (file)
@@ -50,10 +50,8 @@ fn main() {
 fn parent(flavor: StrBuf) {
     let args = os::args();
     let args = args.as_slice();
-    let mut p = io::Process::new(args[0].as_slice(), [
-        "child".to_owned(),
-        flavor.to_owned()
-    ]).unwrap();
+    let mut p = io::process::Command::new(args[0].as_slice())
+                                     .arg("child").arg(flavor).spawn().unwrap();
     p.stdin.get_mut_ref().write_str("test1\ntest2\ntest3").unwrap();
     let out = p.wait_with_output().unwrap();
     assert!(out.status.success());
index f87c22bdb57c3d0bf2089514cba17bce176d601e..06151e498f5367f7e0e3819b0bc2486a69dd9096 100644 (file)
@@ -16,7 +16,7 @@
 #[phase(syntax, link)]
 extern crate log;
 
-use std::io::{Process, ProcessConfig};
+use std::io::Command;
 use std::os;
 use std::str;
 
@@ -30,16 +30,11 @@ fn main() {
     }
 
     let env = [("RUST_LOG".to_owned(), "debug".to_owned())];
-    let config = ProcessConfig {
-        program: args[0].as_slice(),
-        args: &["child".to_owned()],
-        env: Some(env.as_slice()),
-        ..ProcessConfig::new()
-    };
-    let p = Process::configure(config).unwrap().wait_with_output().unwrap();
+    let p = Command::new(args[0].as_slice())
+                    .arg("child").env(env.as_slice())
+                    .spawn().unwrap().wait_with_output().unwrap();
     assert!(p.status.success());
     let mut lines = str::from_utf8(p.error.as_slice()).unwrap().lines();
     assert!(lines.next().unwrap().contains("foo"));
     assert!(lines.next().unwrap().contains("bar"));
 }
-
index ac3a9ef2d533089cea13c32ad362136e1f05e3f5..1566b9ed6f19c1d467e67426794bf03966b98521 100644 (file)
@@ -10,7 +10,7 @@
 
 #![feature(asm)]
 
-use std::io::Process;
+use std::io::process::Command;
 use std::os;
 use std::str;
 
@@ -40,12 +40,12 @@ fn main() {
     } else if args.len() > 1 && args[1].as_slice() == "loud" {
         loud_recurse();
     } else {
-        let silent = Process::output(args[0], ["silent".to_owned()]).unwrap();
+        let silent = Command::new(args[0].as_slice()).arg("silent").output().unwrap();
         assert!(!silent.status.success());
         let error = str::from_utf8_lossy(silent.error.as_slice());
         assert!(error.as_slice().contains("has overflowed its stack"));
 
-        let loud = Process::output(args[0], ["loud".to_owned()]).unwrap();
+        let loud = Command::new(args[0].as_slice()).arg("loud").output().unwrap();
         assert!(!loud.status.success());
         let error = str::from_utf8_lossy(silent.error.as_slice());
         assert!(error.as_slice().contains("has overflowed its stack"));
index f41f2619032bc99b60c7d83aaae841b8e0bcafbd..44ff58c151ed6a4c89184b01f1316ab0e978795c 100644 (file)
@@ -24,6 +24,7 @@
 extern crate libc;
 
 use std::io::process;
+use std::io::process::Command;
 use std::io::signal::{Listener, Interrupt};
 
 #[start]
@@ -34,19 +35,12 @@ fn start(argc: int, argv: **u8) -> int {
 fn main() {
     unsafe { libc::setsid(); }
 
-    let config = process::ProcessConfig {
-        program : "/bin/sh",
-        args: &["-c".to_owned(), "read a".to_owned()],
-        detach: true,
-        .. process::ProcessConfig::new()
-    };
-
     // we shouldn't die because of an interrupt
     let mut l = Listener::new();
     l.register(Interrupt).unwrap();
 
     // spawn the child
-    let mut p = process::Process::configure(config).unwrap();
+    let mut p = Command::new("/bin/sh").arg("-c").arg("read a").detached().spawn().unwrap();
 
     // send an interrupt to everyone in our process group
     unsafe { libc::funcs::posix88::signal::kill(0, libc::SIGINT); }
@@ -59,4 +53,3 @@ fn main() {
         process::ExitSignal(..) => fail!()
     }
 }
-
index 45af7d5de3481e6b4be3a24b880e1bf5908a4bed..5e1f9bbaf0cfd1291cc4214ef0435f600b3f762c 100644 (file)
@@ -20,8 +20,7 @@
 
 use std::io;
 use std::io::fs;
-use std::io::process::Process;
-use std::io::process::ProcessConfig;
+use std::io::Command;
 use std::os;
 use std::path::Path;
 
@@ -56,13 +55,11 @@ fn main() {
         assert!(fs::copy(&my_path, &child_path).is_ok());
 
         // run child
-        let p = Process::configure(ProcessConfig {
-            program: child_path.as_str().unwrap(),
-            args: [arg.to_owned()],
-            cwd: Some(&cwd),
-            env: Some(my_env.append_one(env).as_slice()),
-            .. ProcessConfig::new()
-        }).unwrap().wait_with_output().unwrap();
+        let p = Command::new(&child_path)
+                        .arg(arg)
+                        .cwd(&cwd)
+                        .env(my_env.append_one(env).as_slice())
+                        .spawn().unwrap().wait_with_output().unwrap();
 
         // display the output
         assert!(io::stdout().write(p.output.as_slice()).is_ok());
index a0459e6e8c1ada7d6d3f9fea5af9c2101b7543d6..174a441ace57547879e1eb2a9a441cd1b7e5cfbc 100644 (file)
@@ -21,7 +21,7 @@
 // ignore-win32
 
 use std::os;
-use std::io::process::{Process, ExitSignal, ExitStatus};
+use std::io::process::{Command, ExitSignal, ExitStatus};
 
 pub fn main() {
     let args = os::args();
@@ -30,7 +30,7 @@ pub fn main() {
         // Raise a segfault.
         unsafe { *(0 as *mut int) = 0; }
     } else {
-        let status = Process::status(args[0], ["signal".to_owned()]).unwrap();
+        let status = Command::new(args[0].as_slice()).arg("signal").status().unwrap();
         // Windows does not have signal, so we get exit status 0xC0000028 (STATUS_BAD_STACK).
         match status {
             ExitSignal(_) if cfg!(unix) => {},
@@ -39,4 +39,3 @@ pub fn main() {
         }
     }
 }
-
index 2b42e3ada542879fa083edf961c3e66ea55f0820..8e2cfa30066495e00b7241d4022bb0b4ae7da155 100644 (file)
@@ -12,7 +12,8 @@
 // doesn't die in a ball of fire, but rather it's gracefully handled.
 
 use std::os;
-use std::io::{PipeStream, Process};
+use std::io::PipeStream;
+use std::io::Command;
 
 fn test() {
     let os::Pipe { input, out } = os::pipe();
@@ -30,6 +31,7 @@ fn main() {
         return test();
     }
 
-    let mut p = Process::new(args[0], ["test".to_owned()]).unwrap();
+    let mut p = Command::new(args[0].as_slice())
+                        .arg("test").spawn().unwrap();
     assert!(p.wait().unwrap().success());
 }