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