about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-08-24 10:02:18 +0000
committerbors <bors@rust-lang.org>2017-08-24 10:02:18 +0000
commita125ec918fc228fc20da4642d17bc178a99a5d72 (patch)
tree401e62ecf9ebf285bc3572125d8783388895982e /src
parenta12e4f8098438cbfa65361647742e3c026574cb5 (diff)
parent91bfe3f55bddf508624a39eec9da859f911f966c (diff)
downloadrust-a125ec918fc228fc20da4642d17bc178a99a5d72.tar.gz
rust-a125ec918fc228fc20da4642d17bc178a99a5d72.zip
Auto merge of #43907 - euclio:command, r=alexcrichton
Use std::process::Command throughout compile-test

Resubmission of #43798.

Fixes #43762.

r? @alexcrichton
Diffstat (limited to 'src')
-rw-r--r--src/tools/compiletest/src/main.rs1
-rw-r--r--src/tools/compiletest/src/procsrv.rs134
-rw-r--r--src/tools/compiletest/src/runtest.rs556
3 files changed, 240 insertions, 451 deletions
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 384ae3f45f6..20239e97478 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -39,7 +39,6 @@ use util::logv;
 
 use self::header::EarlyProps;
 
-pub mod procsrv;
 pub mod util;
 mod json;
 pub mod header;
diff --git a/src/tools/compiletest/src/procsrv.rs b/src/tools/compiletest/src/procsrv.rs
deleted file mode 100644
index ffcc60e7852..00000000000
--- a/src/tools/compiletest/src/procsrv.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use std::env;
-use std::ffi::OsString;
-use std::io::prelude::*;
-use std::io;
-use std::path::PathBuf;
-use std::process::{Child, Command, ExitStatus, Output, Stdio};
-
-/// Get the name of the environment variable that holds dynamic library
-/// locations
-pub fn dylib_env_var() -> &'static str {
-    if cfg!(windows) {
-        "PATH"
-    } else if cfg!(target_os = "macos") {
-        "DYLD_LIBRARY_PATH"
-    } else if cfg!(target_os = "haiku") {
-        "LIBRARY_PATH"
-    } else {
-        "LD_LIBRARY_PATH"
-    }
-}
-
-/// Add `lib_path` and `aux_path` (if it is `Some`) to the dynamic library
-/// env var
-fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) {
-    // Need to be sure to put both the lib_path and the aux path in the dylib
-    // search path for the child.
-    let var = dylib_env_var();
-    let mut path = env::split_paths(&env::var_os(var).unwrap_or(OsString::new()))
-        .collect::<Vec<_>>();
-    if let Some(p) = aux_path {
-        path.insert(0, PathBuf::from(p))
-    }
-    path.insert(0, PathBuf::from(lib_path));
-
-    // Add the new dylib search path var
-    let newpath = env::join_paths(&path).unwrap();
-    cmd.env(var, newpath);
-}
-
-/// Represents exit status, stdout and stderr of a completed process
-pub struct Result {
-    pub status: ExitStatus,
-    pub out: String,
-    pub err: String,
-}
-
-/// Runs a test program
-///
-/// # Params
-///  - `lib_path` Path to search for required library
-///  - `prog` command to run
-///  - `aux_path` Optional extra path to search for required
-///    auxiliary libraries
-///  - `args` List of arguments to pass to `prog`
-///  - `env` List of environment variables to set, `.0` is variable name,
-///    `.1` is value
-///  - `input` String to be fed as stdin
-///  - `current_dir` Optional working dir to run command in
-///
-pub fn run(lib_path: &str,
-           prog: &str,
-           aux_path: Option<&str>,
-           args: &[String],
-           env: Vec<(String, String)>,
-           input: Option<String>,
-           current_dir: Option<String>)
-           -> io::Result<Result> {
-
-    let mut cmd = Command::new(prog);
-    cmd.args(args)
-        .stdout(Stdio::piped())
-        .stderr(Stdio::piped())
-        .stdin(Stdio::piped());
-
-    add_target_env(&mut cmd, lib_path, aux_path);
-    for (key, val) in env {
-        cmd.env(&key, &val);
-    }
-    if let Some(cwd) = current_dir {
-        cmd.current_dir(cwd);
-    }
-
-    let mut process = cmd.spawn()?;
-    if let Some(input) = input {
-        process.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
-    }
-    let Output { status, stdout, stderr } = process.wait_with_output().unwrap();
-
-    Ok(Result {
-        status,
-        out: String::from_utf8(stdout).unwrap(),
-        err: String::from_utf8(stderr).unwrap(),
-    })
-}
-
-/// Same as `run`, but return process rather than waiting on completion
-pub fn run_background(lib_path: &str,
-                      prog: &str,
-                      aux_path: Option<&str>,
-                      args: &[String],
-                      env: Vec<(String, String)>,
-                      input: Option<String>,
-                      current_dir: Option<String>)
-                      -> io::Result<Child> {
-
-    let mut cmd = Command::new(prog);
-    cmd.args(args)
-       .stdin(Stdio::piped())
-       .stdout(Stdio::piped());
-    add_target_env(&mut cmd, lib_path, aux_path);
-    for (key, val) in env {
-        cmd.env(&key, &val);
-    }
-    if let Some(cwd) = current_dir {
-        cmd.current_dir(cwd);
-    }
-
-    let mut process = cmd.spawn()?;
-    if let Some(input) = input {
-        process.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
-    }
-
-    Ok(process)
-}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 02511ac6d98..d2a0c776b33 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -17,22 +17,35 @@ use errors::{self, ErrorKind, Error};
 use filetime::FileTime;
 use json;
 use header::TestProps;
-use procsrv;
 use test::TestPaths;
 use util::logv;
 
+use std::collections::HashMap;
 use std::collections::HashSet;
 use std::env;
+use std::ffi::OsString;
 use std::fs::{self, File, create_dir_all};
 use std::io::prelude::*;
 use std::io::{self, BufReader};
 use std::path::{Path, PathBuf};
-use std::process::{Command, Output, ExitStatus};
+use std::process::{Command, Output, ExitStatus, Stdio};
 use std::str;
-use std::collections::HashMap;
 
 use extract_gdb_version;
 
+/// The name of the environment variable that holds dynamic library locations.
+pub fn dylib_env_var() -> &'static str {
+    if cfg!(windows) {
+        "PATH"
+    } else if cfg!(target_os = "macos") {
+        "DYLD_LIBRARY_PATH"
+    } else if cfg!(target_os = "haiku") {
+        "LIBRARY_PATH"
+    } else {
+        "LD_LIBRARY_PATH"
+    }
+}
+
 pub fn run(config: Config, testpaths: &TestPaths) {
     match &*config.target {
 
@@ -325,37 +338,23 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn print_source(&self,
-                    src: String,
-                    pretty_type: &str)
-                    -> ProcRes {
+    fn print_source(&self, src: String, pretty_type: &str) -> ProcRes {
         let aux_dir = self.aux_output_dir_name();
-        self.compose_and_run(self.make_pp_args(pretty_type.to_owned()),
-                             self.props.exec_env.clone(),
+
+        let mut rustc = Command::new(&self.config.rustc_path);
+        rustc.arg("-")
+            .arg("-Zunstable-options")
+            .args(&["--unpretty", &pretty_type])
+            .args(&["--target", &self.config.target])
+            .arg("-L").arg(&aux_dir)
+            .args(self.split_maybe_args(&self.config.target_rustcflags))
+            .args(&self.props.compile_flags)
+            .envs(self.props.exec_env.clone());
+
+        self.compose_and_run(rustc,
                              self.config.compile_lib_path.to_str().unwrap(),
                              Some(aux_dir.to_str().unwrap()),
-                             Some(src),
-                             None)
-    }
-
-    fn make_pp_args(&self,
-                    pretty_type: String)
-                    -> ProcArgs {
-        let aux_dir = self.aux_output_dir_name();
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let mut args = vec!["-".to_owned(),
-                            "-Zunstable-options".to_owned(),
-                            "--unpretty".to_owned(),
-                            pretty_type,
-                            format!("--target={}", self.config.target),
-                            "-L".to_owned(),
-                            aux_dir.to_str().unwrap().to_owned()];
-        args.extend(self.split_maybe_args(&self.config.target_rustcflags));
-        args.extend(self.props.compile_flags.iter().cloned());
-        ProcArgs {
-            prog: self.config.rustc_path.to_str().unwrap().to_owned(),
-            args,
-        }
+                             Some(src))
     }
 
     fn compare_source(&self,
@@ -379,45 +378,35 @@ actual:\n\
     }
 
     fn typecheck_source(&self, src: String) -> ProcRes {
-        let args = self.make_typecheck_args();
-        self.compose_and_run_compiler(args, Some(src))
-    }
+        let mut rustc = Command::new(&self.config.rustc_path);
+
+        let out_dir = self.output_base_name().with_extension("pretty-out");
+        let _ = fs::remove_dir_all(&out_dir);
+        create_dir_all(&out_dir).unwrap();
 
-    fn make_typecheck_args(&self) -> ProcArgs {
-        let aux_dir = self.aux_output_dir_name();
         let target = if self.props.force_host {
             &*self.config.host
         } else {
             &*self.config.target
         };
 
-        let out_dir = self.output_base_name().with_extension("pretty-out");
-        let _ = fs::remove_dir_all(&out_dir);
-        create_dir_all(&out_dir).unwrap();
+        let aux_dir = self.aux_output_dir_name();
+
+        rustc.arg("-")
+            .arg("-Zno-trans")
+            .arg("--out-dir").arg(&out_dir)
+            .arg(&format!("--target={}", target))
+            .arg("-L").arg(&self.config.build_base)
+            .arg("-L").arg(aux_dir);
 
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let mut args = vec!["-".to_owned(),
-                            "-Zno-trans".to_owned(),
-                            "--out-dir".to_owned(),
-                            out_dir.to_str().unwrap().to_owned(),
-                            format!("--target={}", target),
-                            "-L".to_owned(),
-                            self.config.build_base.to_str().unwrap().to_owned(),
-                            "-L".to_owned(),
-                            aux_dir.to_str().unwrap().to_owned()];
         if let Some(revision) = self.revision {
-            args.extend(vec![
-                "--cfg".to_string(),
-                revision.to_string(),
-            ]);
-        }
-        args.extend(self.split_maybe_args(&self.config.target_rustcflags));
-        args.extend(self.props.compile_flags.iter().cloned());
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        ProcArgs {
-            prog: self.config.rustc_path.to_str().unwrap().to_owned(),
-            args,
+            rustc.args(&["--cfg", revision]);
         }
+
+        rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
+        rustc.args(&self.props.compile_flags);
+
+        self.compose_and_run_compiler(rustc, Some(src))
     }
 
     fn run_debuginfo_gdb_test(&self) {
@@ -500,32 +489,19 @@ actual:\n\
                 debug!("script_str = {}", script_str);
                 self.dump_output_file(&script_str, "debugger.script");
 
+                let adb_path = &self.config.adb_path;
 
-                procsrv::run("",
-                             &self.config.adb_path,
-                             None,
-                             &[
-                                 "push".to_owned(),
-                                 exe_file.to_str().unwrap().to_owned(),
-                                 self.config.adb_test_dir.clone()
-                             ],
-                             Vec::new(),
-                             None,
-                             None)
-                    .expect(&format!("failed to exec `{:?}`", self.config.adb_path));
-
-                procsrv::run("",
-                             &self.config.adb_path,
-                             None,
-                             &[
-                                 "forward".to_owned(),
-                                 "tcp:5039".to_owned(),
-                                 "tcp:5039".to_owned()
-                             ],
-                             Vec::new(),
-                             None,
-                             None)
-                    .expect(&format!("failed to exec `{:?}`", self.config.adb_path));
+                Command::new(adb_path)
+                    .arg("push")
+                    .arg(&exe_file)
+                    .arg(&self.config.adb_test_dir)
+                    .status()
+                    .expect(&format!("failed to exec `{:?}`", adb_path));
+
+                Command::new(adb_path)
+                    .args(&["forward", "tcp:5039", "tcp:5039"])
+                    .status()
+                    .expect(&format!("failed to exec `{:?}`", adb_path));
 
                 let adb_arg = format!("export LD_LIBRARY_PATH={}; \
                                        gdbserver{} :5039 {}/{}",
@@ -537,23 +513,17 @@ actual:\n\
                                       .unwrap());
 
                 debug!("adb arg: {}", adb_arg);
-                let mut process = procsrv::run_background("",
-                                                          &self.config.adb_path
-                                                          ,
-                                                          None,
-                                                          &[
-                                                              "shell".to_owned(),
-                                                              adb_arg.clone()
-                                                          ],
-                                                          Vec::new(),
-                                                          None,
-                                                          None)
-                    .expect(&format!("failed to exec `{:?}`", self.config.adb_path));
+                let mut adb = Command::new(adb_path)
+                    .args(&["shell", &adb_arg])
+                    .stdout(Stdio::piped())
+                    .stderr(Stdio::inherit())
+                    .spawn()
+                    .expect(&format!("failed to exec `{:?}`", adb_path));
 
                 // Wait for the gdbserver to print out "Listening on port ..."
                 // at which point we know that it's started and then we can
                 // execute the debugger below.
-                let mut stdout = BufReader::new(process.stdout.take().unwrap());
+                let mut stdout = BufReader::new(adb.stdout.take().unwrap());
                 let mut line = String::new();
                 loop {
                     line.truncate(0);
@@ -574,33 +544,29 @@ actual:\n\
 
                 let mut gdb_path = tool_path;
                 gdb_path.push_str("/bin/gdb");
-                let procsrv::Result {
-                    out,
-                    err,
-                    status
-                } = procsrv::run("",
-                                 &gdb_path,
-                                 None,
-                                 &debugger_opts,
-                                 Vec::new(),
-                                 None,
-                                 None)
+                let Output {
+                    status,
+                    stdout,
+                    stderr
+                } = Command::new(&gdb_path)
+                    .args(&debugger_opts)
+                    .output()
                     .expect(&format!("failed to exec `{:?}`", gdb_path));
                 let cmdline = {
-                    let cmdline = self.make_cmdline("",
-                                                    &format!("{}-gdb", self.config.target),
-                                                    &debugger_opts);
+                    let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
+                    gdb.args(&debugger_opts);
+                    let cmdline = self.make_cmdline(&gdb, "");
                     logv(self.config, format!("executing {}", cmdline));
                     cmdline
                 };
 
                 debugger_run_result = ProcRes {
                     status,
-                    stdout: out,
-                    stderr: err,
+                    stdout: String::from_utf8(stdout).unwrap(),
+                    stderr: String::from_utf8(stderr).unwrap(),
                     cmdline,
                 };
-                if process.kill().is_err() {
+                if adb.kill().is_err() {
                     println!("Adb process is already finished.");
                 }
             }
@@ -679,19 +645,14 @@ actual:\n\
                          "-nx".to_owned(),
                          format!("-command={}", debugger_script.to_str().unwrap())];
 
-                let proc_args = ProcArgs {
-                    prog: self.config.gdb.as_ref().unwrap().to_owned(),
-                    args: debugger_opts,
-                };
-
-                let environment = vec![("PYTHONPATH".to_owned(), rust_pp_module_abs_path)];
+                let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
+                gdb.args(&debugger_opts)
+                    .env("PYTHONPATH", rust_pp_module_abs_path);
 
                 debugger_run_result =
-                    self.compose_and_run(proc_args,
-                                         environment,
+                    self.compose_and_run(gdb,
                                          self.config.run_lib_path.to_str().unwrap(),
                                          None,
-                                         None,
                                          None);
             }
         }
@@ -1154,10 +1115,11 @@ actual:\n\
     }
 
     fn compile_test(&self) -> ProcRes {
-        let aux_dir = self.aux_output_dir_name();
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let mut extra_args = vec!["-L".to_owned(),
-                                  aux_dir.to_str().unwrap().to_owned()];
+        let mut rustc = self.make_compile_args(
+            &self.testpaths.file, TargetLocation::ThisFile(self.make_exe_name()));
+
+        rustc.arg("-L").arg(&self.aux_output_dir_name());
+
         match self.config.mode {
             CompileFail | Ui => {
                 // compile-fail and ui tests tend to have tons of unused code as
@@ -1165,15 +1127,12 @@ actual:\n\
                 // want to actually assert warnings about all this code. Instead
                 // let's just ignore unused code warnings by defaults and tests
                 // can turn it back on if needed.
-                extra_args.push("-A".to_owned());
-                extra_args.push("unused".to_owned());
+                rustc.args(&["-A", "unused"]);
             }
             _ => {}
         }
-        let args = self.make_compile_args(extra_args,
-                                          &self.testpaths.file,
-                                          TargetLocation::ThisFile(self.make_exe_name()));
-        self.compose_and_run_compiler(args, None)
+
+        self.compose_and_run_compiler(rustc, None)
     }
 
     fn document(&self, out_dir: &Path) -> ProcRes {
@@ -1197,22 +1156,20 @@ actual:\n\
         }
 
         let aux_dir = self.aux_output_dir_name();
-        let mut args = vec!["-L".to_owned(),
-                            aux_dir.to_str().unwrap().to_owned(),
-                            "-o".to_owned(),
-                            out_dir.to_str().unwrap().to_owned(),
-                            self.testpaths.file.to_str().unwrap().to_owned()];
-        args.extend(self.props.compile_flags.iter().cloned());
-        let args = ProcArgs {
-            prog: self.config.rustdoc_path
-                .as_ref().expect("--rustdoc-path passed").to_str().unwrap().to_owned(),
-            args,
-        };
-        self.compose_and_run_compiler(args, None)
+
+        let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed");
+        let mut rustdoc = Command::new(rustdoc_path);
+
+        rustdoc.arg("-L").arg(aux_dir)
+            .arg("-o").arg(out_dir)
+            .arg(&self.testpaths.file)
+            .args(&self.props.compile_flags);
+
+        self.compose_and_run_compiler(rustdoc, None)
     }
 
     fn exec_compiled_test(&self) -> ProcRes {
-        let env = self.props.exec_env.clone();
+        let env = &self.props.exec_env;
 
         match &*self.config.target {
             // This is pretty similar to below, we're transforming:
@@ -1230,41 +1187,39 @@ actual:\n\
             // the process) and then report back the same result.
             _ if self.config.remote_test_client.is_some() => {
                 let aux_dir = self.aux_output_dir_name();
-                let mut args = self.make_run_args();
-                let mut program = args.prog.clone();
+                let ProcArgs { mut prog, args } = self.make_run_args();
                 if let Ok(entries) = aux_dir.read_dir() {
                     for entry in entries {
                         let entry = entry.unwrap();
                         if !entry.path().is_file() {
                             continue
                         }
-                        program.push_str(":");
-                        program.push_str(entry.path().to_str().unwrap());
+                        prog.push_str(":");
+                        prog.push_str(entry.path().to_str().unwrap());
                     }
                 }
-                args.args.insert(0, program);
-                args.args.insert(0, "run".to_string());
-                args.prog = self.config.remote_test_client.clone().unwrap()
-                                .into_os_string().into_string().unwrap();
-                self.compose_and_run(args,
-                                     env,
+                let mut test_client = Command::new(
+                    self.config.remote_test_client.as_ref().unwrap());
+                test_client
+                    .args(&["run", &prog])
+                    .args(args)
+                    .envs(env.clone());
+                self.compose_and_run(test_client,
                                      self.config.run_lib_path.to_str().unwrap(),
                                      Some(aux_dir.to_str().unwrap()),
-                                     None,
                                      None)
             }
             _ => {
                 let aux_dir = self.aux_output_dir_name();
-                let working_dir =
-                    Some(self.output_base_name()
-                             .parent().unwrap()
-                             .to_str().unwrap().to_owned());
-                self.compose_and_run(self.make_run_args(),
-                                     env,
+                let ProcArgs { prog, args } = self.make_run_args();
+                let mut program = Command::new(&prog);
+                program.args(args)
+                    .current_dir(&self.output_base_name().parent().unwrap())
+                    .envs(env.clone());
+                self.compose_and_run(program,
                                      self.config.run_lib_path.to_str().unwrap(),
                                      Some(aux_dir.to_str().unwrap()),
-                                     None,
-                                     working_dir)
+                                     None)
             }
         }
     }
@@ -1293,23 +1248,33 @@ actual:\n\
         }
     }
 
-    fn compose_and_run_compiler(&self, args: ProcArgs, input: Option<String>) -> ProcRes {
+    fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
         if !self.props.aux_builds.is_empty() {
             create_dir_all(&self.aux_output_dir_name()).unwrap();
         }
 
         let aux_dir = self.aux_output_dir_name();
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let extra_link_args = vec!["-L".to_owned(),
-                                   aux_dir.to_str().unwrap().to_owned()];
 
         for rel_ab in &self.props.aux_builds {
             let aux_testpaths = self.compute_aux_test_paths(rel_ab);
             let aux_props = self.props.from_aux_file(&aux_testpaths.file,
                                                      self.revision,
                                                      self.config);
-            let mut crate_type = if aux_props.no_prefer_dynamic {
-                Vec::new()
+            let aux_output = {
+                let f = self.make_lib_name(&self.testpaths.file);
+                let parent = f.parent().unwrap();
+                TargetLocation::ThisDirectory(parent.to_path_buf())
+            };
+            let aux_cx = TestCx {
+                config: self.config,
+                props: &aux_props,
+                testpaths: &aux_testpaths,
+                revision: self.revision
+            };
+            let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output);
+
+            let crate_type = if aux_props.no_prefer_dynamic {
+                None
             } else if (self.config.target.contains("musl") && !aux_props.force_host) ||
                       self.config.target.contains("emscripten") {
                 // We primarily compile all auxiliary libraries as dynamic libraries
@@ -1321,28 +1286,20 @@ actual:\n\
                 // dynamic libraries so we just go back to building a normal library. Note,
                 // however, that for MUSL if the library is built with `force_host` then
                 // it's ok to be a dylib as the host should always support dylibs.
-                vec!["--crate-type=lib".to_owned()]
+                Some("lib")
             } else {
-                vec!["--crate-type=dylib".to_owned()]
-            };
-            crate_type.extend(extra_link_args.clone());
-            let aux_output = {
-                let f = self.make_lib_name(&self.testpaths.file);
-                let parent = f.parent().unwrap();
-                TargetLocation::ThisDirectory(parent.to_path_buf())
+                Some("dylib")
             };
-            let aux_cx = TestCx {
-                config: self.config,
-                props: &aux_props,
-                testpaths: &aux_testpaths,
-                revision: self.revision
-            };
-            let aux_args = aux_cx.make_compile_args(crate_type, &aux_testpaths.file, aux_output);
-            let auxres = aux_cx.compose_and_run(aux_args,
-                                                Vec::new(),
+
+            if let Some(crate_type) = crate_type {
+                aux_rustc.args(&["--crate-type", crate_type]);
+            }
+
+            aux_rustc.arg("-L").arg(&aux_dir);
+
+            let auxres = aux_cx.compose_and_run(aux_rustc,
                                                 aux_cx.config.compile_lib_path.to_str().unwrap(),
                                                 Some(aux_dir.to_str().unwrap()),
-                                                None,
                                                 None);
             if !auxres.status.success() {
                 self.fatal_proc_rec(
@@ -1352,40 +1309,65 @@ actual:\n\
             }
         }
 
-        self.compose_and_run(args,
-                             self.props.rustc_env.clone(),
+        rustc.envs(self.props.rustc_env.clone());
+        self.compose_and_run(rustc,
                              self.config.compile_lib_path.to_str().unwrap(),
                              Some(aux_dir.to_str().unwrap()),
-                             input,
-                             None)
+                             input)
     }
 
     fn compose_and_run(&self,
-                       ProcArgs{ args, prog }: ProcArgs,
-                       procenv: Vec<(String, String)> ,
+                       mut command: Command,
                        lib_path: &str,
                        aux_path: Option<&str>,
-                       input: Option<String>,
-                       working_dir: Option<String>) -> ProcRes {
-        self.program_output(lib_path, prog, aux_path, args, procenv, input, working_dir)
-    }
+                       input: Option<String>) -> ProcRes {
+        let cmdline =
+        {
+            let cmdline = self.make_cmdline(&command, lib_path);
+            logv(self.config, format!("executing {}", cmdline));
+            cmdline
+        };
 
-    fn make_compile_args(&self,
-                         extras: Vec<String> ,
-                         input_file: &Path,
-                         output_file: TargetLocation)
-                         -> ProcArgs
-    {
-        let target = if self.props.force_host {
-            &*self.config.host
-        } else {
-            &*self.config.target
+        command
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .stdin(Stdio::piped());
+
+        // Need to be sure to put both the lib_path and the aux path in the dylib
+        // search path for the child.
+        let mut path = env::split_paths(&env::var_os(dylib_env_var()).unwrap_or(OsString::new()))
+            .collect::<Vec<_>>();
+        if let Some(p) = aux_path {
+            path.insert(0, PathBuf::from(p))
+        }
+        path.insert(0, PathBuf::from(lib_path));
+
+        // Add the new dylib search path var
+        let newpath = env::join_paths(&path).unwrap();
+        command.env(dylib_env_var(), newpath);
+
+        let mut child = command.spawn().expect(&format!("failed to exec `{:?}`", &command));
+        if let Some(input) = input {
+            child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
+        }
+        let Output { status, stdout, stderr } = child.wait_with_output().unwrap();
+
+        let result = ProcRes {
+            status,
+            stdout: String::from_utf8(stdout).unwrap(),
+            stderr: String::from_utf8(stderr).unwrap(),
+            cmdline,
         };
 
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let mut args = vec![input_file.to_str().unwrap().to_owned(),
-                            "-L".to_owned(),
-                            self.config.build_base.to_str().unwrap().to_owned()];
+        self.dump_output(&result.stdout, &result.stderr);
+
+        result
+    }
+
+    fn make_compile_args(&self, input_file: &Path, output_file: TargetLocation) -> Command {
+        let mut rustc = Command::new(&self.config.rustc_path);
+        rustc.arg(input_file)
+            .arg("-L").arg(&self.config.build_base);
 
         // Optionally prevent default --target if specified in test compile-flags.
         let custom_target = self.props.compile_flags
@@ -1393,26 +1375,23 @@ actual:\n\
             .fold(false, |acc, x| acc || x.starts_with("--target"));
 
         if !custom_target {
-            args.extend(vec![
-                format!("--target={}", target),
-            ]);
+            let target = if self.props.force_host {
+                &*self.config.host
+            } else {
+                &*self.config.target
+            };
+
+            rustc.arg(&format!("--target={}", target));
         }
 
         if let Some(revision) = self.revision {
-            args.extend(vec![
-                "--cfg".to_string(),
-                revision.to_string(),
-            ]);
+            rustc.args(&["--cfg", revision]);
         }
 
         if let Some(ref incremental_dir) = self.props.incremental_dir {
-            args.extend(vec![
-                "-Z".to_string(),
-                format!("incremental={}", incremental_dir.display()),
-            ]);
+            rustc.args(&["-Z", &format!("incremental={}", incremental_dir.display())]);
         }
 
-
         match self.config.mode {
             CompileFail |
             ParseFail |
@@ -1421,19 +1400,14 @@ actual:\n\
                 // fashion, then you want JSON mode. Old-skool error
                 // patterns still match the raw compiler output.
                 if self.props.error_patterns.is_empty() {
-                    args.extend(["--error-format",
-                                 "json"]
-                                .iter()
-                                .map(|s| s.to_string()));
+                    rustc.args(&["--error-format", "json"]);
                 }
             }
             MirOpt => {
-                args.extend(["-Zdump-mir=all",
-                             "-Zmir-opt-level=3",
-                             "-Zdump-mir-exclude-pass-number"]
-                            .iter()
-                            .map(|s| s.to_string()));
-
+                rustc.args(&[
+                    "-Zdump-mir=all",
+                    "-Zmir-opt-level=3",
+                    "-Zdump-mir-exclude-pass-number"]);
 
                 let mir_dump_dir = self.get_mir_dump_dir();
                 create_dir_all(mir_dump_dir.as_path()).unwrap();
@@ -1441,7 +1415,7 @@ actual:\n\
                 dir_opt.push_str(mir_dump_dir.to_str().unwrap());
                 debug!("dir_opt: {:?}", dir_opt);
 
-                args.push(dir_opt);
+                rustc.arg(dir_opt);
             }
             RunPass |
             RunFail |
@@ -1458,32 +1432,28 @@ actual:\n\
             }
         }
 
-        args.extend_from_slice(&extras);
         if !self.props.no_prefer_dynamic {
-            args.push("-C".to_owned());
-            args.push("prefer-dynamic".to_owned());
+            rustc.args(&["-C", "prefer-dynamic"]);
         }
-        let path = match output_file {
+
+        match output_file {
             TargetLocation::ThisFile(path) => {
-                args.push("-o".to_owned());
-                path
+                rustc.arg("-o").arg(path);
             }
             TargetLocation::ThisDirectory(path) => {
-                args.push("--out-dir".to_owned());
-                path
+                rustc.arg("--out-dir").arg(path);
             }
-        };
-        args.push(path.to_str().unwrap().to_owned());
+        }
+
         if self.props.force_host {
-            args.extend(self.split_maybe_args(&self.config.host_rustcflags));
+            rustc.args(self.split_maybe_args(&self.config.host_rustcflags));
         } else {
-            args.extend(self.split_maybe_args(&self.config.target_rustcflags));
-        }
-        args.extend(self.props.compile_flags.iter().cloned());
-        ProcArgs {
-            prog: self.config.rustc_path.to_str().unwrap().to_owned(),
-            args,
+            rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
         }
+
+        rustc.args(&self.props.compile_flags);
+
+        rustc
     }
 
     fn make_lib_name(&self, auxfile: &Path) -> PathBuf {
@@ -1554,50 +1524,12 @@ actual:\n\
         }
     }
 
-    fn program_output(&self,
-                      lib_path: &str,
-                      prog: String,
-                      aux_path: Option<&str>,
-                      args: Vec<String>,
-                      env: Vec<(String, String)>,
-                      input: Option<String>,
-                      working_dir: Option<String>)
-                      -> ProcRes {
-        let cmdline =
-        {
-            let cmdline = self.make_cmdline(lib_path,
-                                            &prog,
-                                            &args);
-            logv(self.config, format!("executing {}", cmdline));
-            cmdline
-        };
-
-        let procsrv::Result {
-            out,
-            err,
-            status
-        } = procsrv::run(lib_path,
-                         &prog,
-                         aux_path,
-                         &args,
-                         env,
-                         input,
-                         working_dir).expect(&format!("failed to exec `{}`", prog));
-        self.dump_output(&out, &err);
-        ProcRes {
-            status,
-            stdout: out,
-            stderr: err,
-            cmdline,
-        }
-    }
-
-    fn make_cmdline(&self, libpath: &str, prog: &str, args: &[String]) -> String {
+    fn make_cmdline(&self, command: &Command, libpath: &str) -> String {
         use util;
 
         // Linux and mac don't require adjusting the library search path
         if cfg!(unix) {
-            format!("{} {}", prog, args.join(" "))
+            format!("{:?}", command)
         } else {
             // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
             // for diagnostic purposes
@@ -1605,7 +1537,7 @@ actual:\n\
                 format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
             }
 
-            format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.join(" "))
+            format!("{} {:?}", lib_path_cmd_prefix(libpath), command)
         }
     }
 
@@ -1723,30 +1655,22 @@ actual:\n\
 
     fn compile_test_and_save_ir(&self) -> ProcRes {
         let aux_dir = self.aux_output_dir_name();
-        // FIXME (#9639): This needs to handle non-utf8 paths
-        let mut link_args = vec!["-L".to_owned(),
-                                 aux_dir.to_str().unwrap().to_owned()];
-        let llvm_args = vec!["--emit=llvm-ir".to_owned(),];
-        link_args.extend(llvm_args);
-        let args = self.make_compile_args(link_args,
-                                          &self.testpaths.file,
-                                          TargetLocation::ThisDirectory(
-                                              self.output_base_name().parent()
-                                                                     .unwrap()
-                                                                     .to_path_buf()));
-        self.compose_and_run_compiler(args, None)
+
+        let output_file = TargetLocation::ThisDirectory(
+            self.output_base_name().parent().unwrap().to_path_buf());
+        let mut rustc = self.make_compile_args(&self.testpaths.file, output_file);
+        rustc.arg("-L").arg(aux_dir)
+            .arg("--emit=llvm-ir");
+
+        self.compose_and_run_compiler(rustc, None)
     }
 
     fn check_ir_with_filecheck(&self) -> ProcRes {
         let irfile = self.output_base_name().with_extension("ll");
-        let prog = self.config.llvm_filecheck.as_ref().unwrap();
-        let proc_args = ProcArgs {
-            // FIXME (#9639): This needs to handle non-utf8 paths
-            prog: prog.to_str().unwrap().to_owned(),
-            args: vec![format!("-input-file={}", irfile.to_str().unwrap()),
-                       self.testpaths.file.to_str().unwrap().to_owned()]
-        };
-        self.compose_and_run(proc_args, Vec::new(), "", None, None, None)
+        let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
+        filecheck.arg("--input-file").arg(irfile)
+            .arg(&self.testpaths.file);
+        self.compose_and_run(filecheck, "", None, None)
     }
 
     fn run_codegen_test(&self) {
@@ -2179,7 +2103,7 @@ actual:\n\
            .env("RUSTDOC",
                cwd.join(&self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed")))
            .env("TMPDIR", &tmpdir)
-           .env("LD_LIB_PATH_ENVVAR", procsrv::dylib_env_var())
+           .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
            .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
            .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
            .env("LLVM_COMPONENTS", &self.config.llvm_components)