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)> {
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();
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();
}
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());
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();
status: status,
stdout: out,
stderr: err,
- cmdline: commandline
+ cmdline: format!("{}", cmd)
};
}
}
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
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>
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;
/// 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());
}
}
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); }
}
#[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,
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",
}
unsafe {
-
let mut si = zeroed_startupinfo();
si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
si.dwFlags = STARTF_USESTDHANDLES;
}
}
- 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());
}
}
#[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();
}
#[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;
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);
}
}
- match config.gid {
+ match cfg.gid {
Some(u) => {
if libc::setgid(u as libc::gid_t) != 0 {
fail(&mut output);
}
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
}
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.
}
#[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)
}
#[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.
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);
}
}
#[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())
}
#[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()
);
}
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;
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{}",
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");
}
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;
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;
}
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());
}
},
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();
}
}
* 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(|| {
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),
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());
}
},
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();
}
}
// 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));
}
}
-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
// 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
//
// 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
// 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
// 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.
//
// [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
// 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
//
// 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
// 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.
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");
}
}
// 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
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
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());
}
}
// 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
// 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
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");
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;
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?"
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};
///
/// 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());
}
}
- 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 {
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);
}
}
-/// 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 {
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};
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;
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| {
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};
use prelude::*;
+use std::str;
use fmt;
use io::IoResult;
use io;
use libc;
use mem;
use owned::Box;
-use rt::rtio::{RtioProcess, IoFactory, LocalIo};
+use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
+use c_str::CString;
/// Signal a process to exit, without forcibly killing it. Corresponds to
/// SIGTERM on unix platforms.
/// Representation of a running or exited child process.
///
-/// This structure is used to create, run, and manage child processes. A process
-/// is configured with the `ProcessConfig` struct which contains specific
-/// options for dictating how the child is spawned.
+/// This structure is used to represent and manage child processes. A child
+/// process is created via the `Command` struct, which configures the spawning
+/// process and can itself be constructed using a builder-style interface.
///
/// # Example
///
/// ```should_fail
-/// use std::io::Process;
+/// use std::io::Command;
///
-/// let mut child = match Process::new("/bin/cat", ["file.txt".to_owned()]) {
+/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() {
/// Ok(child) => child,
/// Err(e) => fail!("failed to execute child: {}", e),
/// };
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.
}
}
-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
///
/// ```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));
#[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());
#[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(..) => {}
}
#[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));
#[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() {
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());
#[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);
#[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());
})
// 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!()
}
#[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());
#[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());
#[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();
})
#[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();
// 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();
})
#[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"))]
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();
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();
})
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();
#[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() {
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};
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,
}
// 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<()>;
pub mod dl {
use libc;
use os;
- use path::GenericPath;
- use path;
use ptr;
use result::{Ok, Err, Result};
#[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
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.
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());
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
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;");
// 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
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
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);
// 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());
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;
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();
"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();
"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();
"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();
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) }
use std::io::timer;
use libc;
use std::str;
- use std::io::process::{Process, ProcessOutput};
+ use std::io::process::Command;
use native;
use super::*;
#[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() {
})
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;
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());
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?");
}
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());
}
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());
#[phase(syntax, link)]
extern crate log;
-use std::io::{Process, ProcessConfig};
+use std::io::Command;
use std::os;
use std::str;
}
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"));
}
-
#![feature(asm)]
-use std::io::Process;
+use std::io::process::Command;
use std::os;
use std::str;
} 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"));
extern crate libc;
use std::io::process;
+use std::io::process::Command;
use std::io::signal::{Listener, Interrupt};
#[start]
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); }
process::ExitSignal(..) => fail!()
}
}
-
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;
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());
// 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();
// 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) => {},
}
}
}
-
// 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();
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());
}