about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/compiletest/procsrv.rs20
-rw-r--r--src/compiletest/runtest.rs30
-rw-r--r--src/libnative/io/mod.rs7
-rw-r--r--src/libnative/io/process.rs185
-rw-r--r--src/librustc/back/archive.rs28
-rw-r--r--src/librustc/back/link.rs209
-rw-r--r--src/librustdoc/test.rs6
-rw-r--r--src/librustuv/process.rs104
-rw-r--r--src/librustuv/uvio.rs7
-rw-r--r--src/libstd/c_str.rs54
-rw-r--r--src/libstd/io/mod.rs2
-rw-r--r--src/libstd/io/process.rs595
-rw-r--r--src/libstd/path/posix.rs21
-rw-r--r--src/libstd/path/windows.rs23
-rw-r--r--src/libstd/rt/rtio.rs59
-rw-r--r--src/libstd/unstable/dynamic_lib.rs33
-rw-r--r--src/libworkcache/lib.rs7
-rw-r--r--src/test/auxiliary/linkage-visibility.rs3
-rw-r--r--src/test/run-make/unicode-input/multiple_files.rs7
-rw-r--r--src/test/run-make/unicode-input/span_length.rs8
-rw-r--r--src/test/run-pass/backtrace.rs31
-rw-r--r--src/test/run-pass/core-run-destroy.rs12
-rw-r--r--src/test/run-pass/issue-10626.rs15
-rw-r--r--src/test/run-pass/issue-13304.rs6
-rw-r--r--src/test/run-pass/logging-separate-lines.rs13
-rw-r--r--src/test/run-pass/out-of-stack.rs6
-rw-r--r--src/test/run-pass/process-detach.rs11
-rw-r--r--src/test/run-pass/process-spawn-with-unicode-params.rs15
-rw-r--r--src/test/run-pass/signal-exit-status.rs5
-rw-r--r--src/test/run-pass/sigpipe-should-be-ignored.rs6
30 files changed, 792 insertions, 736 deletions
diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs
index d3642a939db..27a31ea909f 100644
--- a/src/compiletest/procsrv.rs
+++ b/src/compiletest/procsrv.rs
@@ -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();
diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs
index dab1185435d..fbbfcf94eb1 100644
--- a/src/compiletest/runtest.rs
+++ b/src/compiletest/runtest.rs
@@ -420,7 +420,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
 }
 
 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_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
             status: status,
             stdout: out,
             stderr: err,
-            cmdline: commandline
+            cmdline: format!("{}", cmd)
         };
     }
 }
diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index a9aca656319..0c103bc4695 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -27,13 +27,12 @@ use std::c_str::CString;
 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 @@ impl rtio::IoFactory for IoFactory {
     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>
diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs
index 799db64e688..0eebd3380e6 100644
--- a/src/libnative/io/process.rs
+++ b/src/libnative/io/process.rs
@@ -15,9 +15,10 @@ use std::mem;
 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 @@ impl Process {
         }
 
         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 make_command_line(prog: &str, args: &[~str]) -> ~str {
 }
 
 #[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 @@ fn spawn_process_os(config: p::ProcessConfig,
         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 spawn_process_os(config: p::ProcessConfig,
                 }
             }
 
-            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 spawn_process_os(config: p::ProcessConfig,
                 }
                 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 spawn_process_os(config: p::ProcessConfig,
                 }
                 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 spawn_process_os(config: p::ProcessConfig,
 }
 
 #[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()
         );
     }
diff --git a/src/librustc/back/archive.rs b/src/librustc/back/archive.rs
index 04ce4f88831..571959d812a 100644
--- a/src/librustc/back/archive.rs
+++ b/src/librustc/back/archive.rs
@@ -16,7 +16,7 @@ use metadata::filesearch;
 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");
         }
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index de6b5925edb..4d70ecb888f 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -29,7 +29,7 @@ use util::sha2::{Digest, Sha256};
 
 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 @@ pub mod write {
     }
 
     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 mod write {
                 }
             },
             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 @@ pub mod write {
  *    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_upstream_rust_crates(args: &mut Vec<StrBuf>, sess: &Session,
                         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_upstream_rust_crates(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_upstream_rust_crates(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");
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 9e63848b90e..024f5a1b3fd 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -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?"
diff --git a/src/librustuv/process.rs b/src/librustuv/process.rs
index 7afac680151..f6fcf3e4816 100644
--- a/src/librustuv/process.rs
+++ b/src/librustuv/process.rs
@@ -13,7 +13,8 @@ use libc;
 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 @@ impl Process {
             }
         }
 
-        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 @@ impl Process {
                     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 {
diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs
index c42b17cc325..1b8175448fc 100644
--- a/src/librustuv/uvio.rs
+++ b/src/librustuv/uvio.rs
@@ -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::{O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, S_IRUSR,
 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 @@ impl IoFactory for UvIoFactory {
         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| {
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs
index 0885a7af00b..0c529ee4d96 100644
--- a/src/libstd/c_str.rs
+++ b/src/libstd/c_str.rs
@@ -82,6 +82,7 @@ use slice::{ImmutableVector, MutableVector};
 use slice;
 use str::StrSlice;
 use str;
+use strbuf::StrBuf;
 
 /// The representation of a C String.
 ///
@@ -292,6 +293,14 @@ pub trait ToCStr {
     }
 }
 
+// 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 ~str and StrBuf, so that we can
+// take ToCStr as owned. When DST lands, the string instances should
+// be revisted, and arguments bound by ToCStr should be passed by
+// reference.
+
 impl<'a> ToCStr for &'a str {
     #[inline]
     fn to_c_str(&self) -> CString {
@@ -314,6 +323,51 @@ impl<'a> ToCStr for &'a str {
     }
 }
 
+impl ToCStr for ~str {
+    #[inline]
+    fn to_c_str(&self) -> CString {
+        self.as_bytes().to_c_str()
+    }
+
+    #[inline]
+    unsafe fn to_c_str_unchecked(&self) -> CString {
+        self.as_bytes().to_c_str_unchecked()
+    }
+
+    #[inline]
+    fn with_c_str<T>(&self, f: |*libc::c_char| -> T) -> T {
+        self.as_bytes().with_c_str(f)
+    }
+
+    #[inline]
+    unsafe fn with_c_str_unchecked<T>(&self, f: |*libc::c_char| -> T) -> T {
+        self.as_bytes().with_c_str_unchecked(f)
+    }
+}
+
+
+impl ToCStr for StrBuf {
+    #[inline]
+    fn to_c_str(&self) -> CString {
+        self.as_bytes().to_c_str()
+    }
+
+    #[inline]
+    unsafe fn to_c_str_unchecked(&self) -> CString {
+        self.as_bytes().to_c_str_unchecked()
+    }
+
+    #[inline]
+    fn with_c_str<T>(&self, f: |*libc::c_char| -> T) -> T {
+        self.as_bytes().with_c_str(f)
+    }
+
+    #[inline]
+    unsafe fn with_c_str_unchecked<T>(&self, f: |*libc::c_char| -> T) -> T {
+        self.as_bytes().with_c_str_unchecked(f)
+    }
+}
+
 // The length of the stack allocated buffer for `vec.with_c_str()`
 static BUF_LEN: uint = 128;
 
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 2ee3ee59731..5c9d5feab10 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -245,7 +245,7 @@ pub use self::net::tcp::TcpListener;
 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};
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index 349cac723ff..fe51615285a 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -14,13 +14,15 @@
 
 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.
@@ -37,16 +39,16 @@ use rt::rtio::{RtioProcess, IoFactory, LocalIo};
 
 /// 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 @@ impl ProcessExit {
     }
 }
 
-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 @@ impl Process {
     ///
     /// ```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 @@ impl Drop for Process {
 
 #[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 @@ mod tests {
         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 @@ mod tests {
 
     #[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 @@ mod tests {
 
     #[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 @@ mod tests {
         // 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 @@ mod tests {
 
     #[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 @@ mod tests {
     #[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 @@ mod tests {
 
     #[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 @@ mod tests {
     })
 
     #[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 @@ mod tests {
         // 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 @@ mod tests {
     })
 
     #[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 @@ mod tests {
         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 @@ mod tests {
         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 @@ mod tests {
     })
 
     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 @@ mod tests {
 
     #[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() {
diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs
index 99a281755e4..4f7132dc6e4 100644
--- a/src/libstd/path/posix.rs
+++ b/src/libstd/path/posix.rs
@@ -79,11 +79,18 @@ impl FromStr for Path {
     }
 }
 
+// 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.
+
 impl ToCStr for Path {
     #[inline]
     fn to_c_str(&self) -> CString {
         // The Path impl guarantees no internal NUL
-        unsafe { self.as_vec().to_c_str_unchecked() }
+        unsafe { self.to_c_str_unchecked() }
     }
 
     #[inline]
@@ -92,6 +99,18 @@ impl ToCStr for Path {
     }
 }
 
+impl<'a> ToCStr for &'a Path {
+    #[inline]
+    fn to_c_str(&self) -> CString {
+        (*self).to_c_str()
+    }
+
+    #[inline]
+    unsafe fn to_c_str_unchecked(&self) -> CString {
+        (*self).to_c_str_unchecked()
+    }
+}
+
 impl<S: Writer> ::hash::Hash<S> for Path {
     #[inline]
     fn hash(&self, state: &mut S) {
diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs
index dfe7241d7af..176788edcc4 100644
--- a/src/libstd/path/windows.rs
+++ b/src/libstd/path/windows.rs
@@ -103,11 +103,18 @@ impl FromStr for Path {
     }
 }
 
+// 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.
+
 impl ToCStr for Path {
     #[inline]
     fn to_c_str(&self) -> CString {
-        // The Path impl guarantees no embedded NULs
-        unsafe { self.as_vec().to_c_str_unchecked() }
+        // The Path impl guarantees no internal NUL
+        unsafe { self.to_c_str_unchecked() }
     }
 
     #[inline]
@@ -116,6 +123,18 @@ impl ToCStr for Path {
     }
 }
 
+impl<'a> ToCStr for &'a Path {
+    #[inline]
+    fn to_c_str(&self) -> CString {
+        (*self).to_c_str()
+    }
+
+    #[inline]
+    unsafe fn to_c_str_unchecked(&self) -> CString {
+        (*self).to_c_str_unchecked()
+    }
+}
+
 impl<S: Writer> ::hash::Hash<S> for Path {
     #[inline]
     fn hash(&self, state: &mut S) {
diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs
index 90f97e59caa..a6c60df2642 100644
--- a/src/libstd/rt/rtio.rs
+++ b/src/libstd/rt/rtio.rs
@@ -29,7 +29,7 @@ use ai = io::net::addrinfo;
 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 @@ pub trait IoFactory {
 
     // 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<()>;
diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs
index 87d531cc627..68bfde84e2a 100644
--- a/src/libstd/unstable/dynamic_lib.rs
+++ b/src/libstd/unstable/dynamic_lib.rs
@@ -45,12 +45,22 @@ impl Drop for DynamicLibrary {
 }
 
 impl DynamicLibrary {
+    // 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: in the `open` method.)
+
     /// Lazily open a dynamic library. When passed None it gives a
     /// handle to the calling process
-    pub fn open(filename: Option<&path::Path>) -> Result<DynamicLibrary, ~str> {
+    pub fn open<T: ToCStr>(filename: Option<T>)
+                        -> Result<DynamicLibrary, ~str> {
         unsafe {
+            let mut filename = filename;
             let maybe_library = dl::check_for_errors_in(|| {
-                match filename {
+                match filename.take() {
                     Some(name) => dl::open_external(name),
                     None => dl::open_internal()
                 }
@@ -114,7 +124,8 @@ mod test {
     fn test_loading_cosine() {
         // The math library does not need to be loaded since it is already
         // statically linked in
-        let libm = match DynamicLibrary::open(None) {
+        let none: Option<Path> = None; // appease the typechecker
+        let libm = match DynamicLibrary::open(none) {
             Err(error) => fail!("Could not load self as module: {}", error),
             Ok(libm) => libm
         };
@@ -142,7 +153,7 @@ mod test {
     fn test_errors_do_not_crash() {
         // Open /dev/null as a library to get an error, and make sure
         // that only causes an error, and not a crash.
-        let path = GenericPath::new("/dev/null");
+        let path = Path::new("/dev/null");
         match DynamicLibrary::open(Some(&path)) {
             Err(_) => {}
             Ok(_) => fail!("Successfully opened the empty library.")
@@ -157,12 +168,11 @@ mod test {
 pub mod dl {
     use c_str::ToCStr;
     use libc;
-    use path;
     use ptr;
     use str;
     use result::*;
 
-    pub unsafe fn open_external(filename: &path::Path) -> *u8 {
+    pub unsafe fn open_external<T: ToCStr>(filename: T) -> *u8 {
         filename.with_c_str(|raw_name| {
             dlopen(raw_name, Lazy as libc::c_int) as *u8
         })
@@ -221,13 +231,16 @@ pub mod dl {
 pub mod dl {
     use libc;
     use os;
-    use path::GenericPath;
-    use path;
     use ptr;
     use result::{Ok, Err, Result};
+    use str;
+    use c_str::ToCStr;
 
-    pub unsafe fn open_external(filename: &path::Path) -> *u8 {
-        os::win32::as_utf16_p(filename.as_str().unwrap(), |raw_name| {
+    pub unsafe fn open_external<T: ToCStr>(filename: T) -> *u8 {
+        // Windows expects Unicode data
+        let filename_cstr = filename.to_c_str();
+        let filename_str = str::from_utf8(filename_cstr.as_bytes_no_nul()).unwrap();
+        os::win32::as_utf16_p(filename_str, |raw_name| {
             LoadLibraryW(raw_name as *libc::c_void) as *u8
         })
     }
diff --git a/src/libworkcache/lib.rs b/src/libworkcache/lib.rs
index c2dd8459540..98fbb00f437 100644
--- a/src/libworkcache/lib.rs
+++ b/src/libworkcache/lib.rs
@@ -488,7 +488,7 @@ impl<'a, T:Send +
 #[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 test() {
         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.
diff --git a/src/test/auxiliary/linkage-visibility.rs b/src/test/auxiliary/linkage-visibility.rs
index ab3539ebf6f..4ae0b6f14f5 100644
--- a/src/test/auxiliary/linkage-visibility.rs
+++ b/src/test/auxiliary/linkage-visibility.rs
@@ -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());
diff --git a/src/test/run-make/unicode-input/multiple_files.rs b/src/test/run-make/unicode-input/multiple_files.rs
index a08d6bb0bf8..219eb1a3ebd 100644
--- a/src/test/run-make/unicode-input/multiple_files.rs
+++ b/src/test/run-make/unicode-input/multiple_files.rs
@@ -12,7 +12,7 @@ extern crate rand;
 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
diff --git a/src/test/run-make/unicode-input/span_length.rs b/src/test/run-make/unicode-input/span_length.rs
index d5614975215..2bb89d76213 100644
--- a/src/test/run-make/unicode-input/span_length.rs
+++ b/src/test/run-make/unicode-input/span_length.rs
@@ -12,7 +12,7 @@ extern crate rand;
 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());
 
diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs
index 260ce69f821..25df896ec15 100644
--- a/src/test/run-pass/backtrace.rs
+++ b/src/test/run-pass/backtrace.rs
@@ -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();
diff --git a/src/test/run-pass/core-run-destroy.rs b/src/test/run-pass/core-run-destroy.rs
index 01a71d862b4..a0d4785d8d2 100644
--- a/src/test/run-pass/core-run-destroy.rs
+++ b/src/test/run-pass/core-run-destroy.rs
@@ -22,7 +22,7 @@ extern crate native;
 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 @@ macro_rules! iotest (
             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 @@ iotest!(fn test_destroy_once() {
 
 #[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 @@ iotest!(fn test_destroy_twice() {
 })
 
 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());
 
diff --git a/src/test/run-pass/issue-10626.rs b/src/test/run-pass/issue-10626.rs
index 38030eb6c1f..dd513547212 100644
--- a/src/test/run-pass/issue-10626.rs
+++ b/src/test/run-pass/issue-10626.rs
@@ -18,7 +18,7 @@ use std::io::process;
 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());
 }
diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs
index 36db5a64555..36fc48432a8 100644
--- a/src/test/run-pass/issue-13304.rs
+++ b/src/test/run-pass/issue-13304.rs
@@ -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());
diff --git a/src/test/run-pass/logging-separate-lines.rs b/src/test/run-pass/logging-separate-lines.rs
index f87c22bdb57..06151e498f5 100644
--- a/src/test/run-pass/logging-separate-lines.rs
+++ b/src/test/run-pass/logging-separate-lines.rs
@@ -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"));
 }
-
diff --git a/src/test/run-pass/out-of-stack.rs b/src/test/run-pass/out-of-stack.rs
index ac3a9ef2d53..1566b9ed6f1 100644
--- a/src/test/run-pass/out-of-stack.rs
+++ b/src/test/run-pass/out-of-stack.rs
@@ -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"));
diff --git a/src/test/run-pass/process-detach.rs b/src/test/run-pass/process-detach.rs
index f41f2619032..44ff58c151e 100644
--- a/src/test/run-pass/process-detach.rs
+++ b/src/test/run-pass/process-detach.rs
@@ -24,6 +24,7 @@ extern crate rustuv;
 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!()
     }
 }
-
diff --git a/src/test/run-pass/process-spawn-with-unicode-params.rs b/src/test/run-pass/process-spawn-with-unicode-params.rs
index 45af7d5de34..5e1f9bbaf0c 100644
--- a/src/test/run-pass/process-spawn-with-unicode-params.rs
+++ b/src/test/run-pass/process-spawn-with-unicode-params.rs
@@ -20,8 +20,7 @@ extern crate native;
 
 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());
diff --git a/src/test/run-pass/signal-exit-status.rs b/src/test/run-pass/signal-exit-status.rs
index a0459e6e8c1..174a441ace5 100644
--- a/src/test/run-pass/signal-exit-status.rs
+++ b/src/test/run-pass/signal-exit-status.rs
@@ -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() {
         }
     }
 }
-
diff --git a/src/test/run-pass/sigpipe-should-be-ignored.rs b/src/test/run-pass/sigpipe-should-be-ignored.rs
index 2b42e3ada54..8e2cfa30066 100644
--- a/src/test/run-pass/sigpipe-should-be-ignored.rs
+++ b/src/test/run-pass/sigpipe-should-be-ignored.rs
@@ -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());
 }