Process::new etc should support non-utf8 commands/args
The existing APIs for spawning processes took strings for the command and arguments, but the underlying system may not impose utf8 encoding, so this is overly limiting. The assumption we actually want to make is just that the command and arguments are viewable as [u8] slices with no interior NULLs, i.e., as CStrings. The ToCStr trait is a handy bound for types that meet this requirement (such as &str and Path). However, since the commands and arguments are often a mixture of strings and paths, it would be inconvenient to take a slice with a single T: ToCStr bound. So this patch revamps the process creation API to instead use a builder-style interface, called `Command`, allowing arguments to be added one at a time with differing ToCStr implementations for each. The initial cut of the builder API has some drawbacks that can be addressed once issue #13851 (libstd as a facade) is closed. These are detailed as FIXMEs. Closes #11650. [breaking-change]
This commit is contained in:
parent
8f9cbe08c6
commit
046062d3bf
27 changed files with 668 additions and 719 deletions
|
|
@ -27,7 +27,8 @@ fn bar() { }
|
|||
fn baz() { }
|
||||
|
||||
pub fn test() {
|
||||
let lib = DynamicLibrary::open(None).unwrap();
|
||||
let none: Option<Path> = None; // appease the typechecker
|
||||
let lib = DynamicLibrary::open(none).unwrap();
|
||||
unsafe {
|
||||
assert!(lib.symbol::<int>("foo").is_ok());
|
||||
assert!(lib.symbol::<int>("baz").is_err());
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ extern crate rand;
|
|||
use rand::{task_rng, Rng};
|
||||
|
||||
use std::{char, os, str};
|
||||
use std::io::{File, Process};
|
||||
use std::io::{File, Command};
|
||||
|
||||
// creates unicode_input_multiple_files_{main,chars}.rs, where the
|
||||
// former imports the latter. `_chars` just contains an indentifier
|
||||
|
|
@ -40,7 +40,6 @@ fn main() {
|
|||
let tmpdir = Path::new(args.get(2).as_slice());
|
||||
|
||||
let main_file = tmpdir.join("unicode_input_multiple_files_main.rs");
|
||||
let main_file_str = main_file.as_str().unwrap();
|
||||
{
|
||||
let _ = File::create(&main_file).unwrap()
|
||||
.write_str("mod unicode_input_multiple_files_chars;");
|
||||
|
|
@ -57,7 +56,9 @@ fn main() {
|
|||
|
||||
// rustc is passed to us with --out-dir and -L etc., so we
|
||||
// can't exec it directly
|
||||
let result = Process::output("sh", ["-c".to_owned(), rustc + " " + main_file_str]).unwrap();
|
||||
let result = Command::new("sh")
|
||||
.arg("-c").arg(rustc + " " + main_file.as_str().unwrap())
|
||||
.output().unwrap();
|
||||
let err = str::from_utf8_lossy(result.error.as_slice());
|
||||
|
||||
// positive test so that this test will be updated when the
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ extern crate rand;
|
|||
use rand::{task_rng, Rng};
|
||||
|
||||
use std::{char, os, str};
|
||||
use std::io::{File, Process};
|
||||
use std::io::{File, Command};
|
||||
|
||||
// creates a file with `fn main() { <random ident> }` and checks the
|
||||
// compiler emits a span of the appropriate length (for the
|
||||
|
|
@ -37,9 +37,7 @@ fn main() {
|
|||
let args = os::args();
|
||||
let rustc = args.get(1).as_slice();
|
||||
let tmpdir = Path::new(args.get(2).as_slice());
|
||||
|
||||
let main_file = tmpdir.join("span_main.rs");
|
||||
let main_file_str = main_file.as_str().unwrap();
|
||||
|
||||
for _ in range(0, 100) {
|
||||
let n = task_rng().gen_range(3u, 20);
|
||||
|
|
@ -53,7 +51,9 @@ fn main() {
|
|||
|
||||
// rustc is passed to us with --out-dir and -L etc., so we
|
||||
// can't exec it directly
|
||||
let result = Process::output("sh", ["-c".to_owned(), rustc + " " + main_file_str]).unwrap();
|
||||
let result = Command::new("sh")
|
||||
.arg("-c").arg(rustc + " " + main_file.as_str().unwrap())
|
||||
.output().unwrap();
|
||||
|
||||
let err = str::from_utf8_lossy(result.error.as_slice());
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
extern crate native;
|
||||
|
||||
use std::os;
|
||||
use std::io::process::{Process, ProcessConfig};
|
||||
use std::io::process::Command;
|
||||
use std::unstable::finally::Finally;
|
||||
use std::str;
|
||||
|
||||
|
|
@ -48,15 +48,7 @@ fn runtest(me: &str) {
|
|||
env.push(("RUST_BACKTRACE".to_strbuf(), "1".to_strbuf()));
|
||||
|
||||
// Make sure that the stack trace is printed
|
||||
let env = env.iter()
|
||||
.map(|&(ref k, ref v)| (k.to_owned(), v.to_owned()))
|
||||
.collect::<Vec<_>>();
|
||||
let mut p = Process::configure(ProcessConfig {
|
||||
program: me,
|
||||
args: ["fail".to_owned()],
|
||||
env: Some(env.as_slice()),
|
||||
.. ProcessConfig::new()
|
||||
}).unwrap();
|
||||
let mut p = Command::new(me).arg("fail").env(env.as_slice()).spawn().unwrap();
|
||||
let out = p.wait_with_output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
let s = str::from_utf8(out.error.as_slice()).unwrap();
|
||||
|
|
@ -64,11 +56,7 @@ fn runtest(me: &str) {
|
|||
"bad output: {}", s);
|
||||
|
||||
// Make sure the stack trace is *not* printed
|
||||
let mut p = Process::configure(ProcessConfig {
|
||||
program: me,
|
||||
args: ["fail".to_owned()],
|
||||
.. ProcessConfig::new()
|
||||
}).unwrap();
|
||||
let mut p = Command::new(me).arg("fail").spawn().unwrap();
|
||||
let out = p.wait_with_output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
let s = str::from_utf8(out.error.as_slice()).unwrap();
|
||||
|
|
@ -76,11 +64,7 @@ fn runtest(me: &str) {
|
|||
"bad output2: {}", s);
|
||||
|
||||
// Make sure a stack trace is printed
|
||||
let mut p = Process::configure(ProcessConfig {
|
||||
program: me,
|
||||
args: ["double-fail".to_owned()],
|
||||
.. ProcessConfig::new()
|
||||
}).unwrap();
|
||||
let mut p = Command::new(me).arg("double-fail").spawn().unwrap();
|
||||
let out = p.wait_with_output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
let s = str::from_utf8(out.error.as_slice()).unwrap();
|
||||
|
|
@ -88,12 +72,7 @@ fn runtest(me: &str) {
|
|||
"bad output3: {}", s);
|
||||
|
||||
// Make sure a stack trace isn't printed too many times
|
||||
let mut p = Process::configure(ProcessConfig {
|
||||
program: me,
|
||||
args: ["double-fail".to_owned()],
|
||||
env: Some(env.as_slice()),
|
||||
.. ProcessConfig::new()
|
||||
}).unwrap();
|
||||
let mut p = Command::new(me).arg("double-fail").env(env.as_slice()).spawn().unwrap();
|
||||
let out = p.wait_with_output().unwrap();
|
||||
assert!(!out.status.success());
|
||||
let s = str::from_utf8(out.error.as_slice()).unwrap();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ extern crate native;
|
|||
extern crate green;
|
||||
extern crate rustuv;
|
||||
|
||||
use std::io::Process;
|
||||
use std::io::{Process, Command};
|
||||
|
||||
macro_rules! succeed( ($e:expr) => (
|
||||
match $e { Ok(..) => {}, Err(e) => fail!("failure: {}", e) }
|
||||
|
|
@ -36,7 +36,7 @@ macro_rules! iotest (
|
|||
use std::io::timer;
|
||||
use libc;
|
||||
use std::str;
|
||||
use std::io::process::{Process, ProcessOutput};
|
||||
use std::io::process::Command;
|
||||
use native;
|
||||
use super::*;
|
||||
|
||||
|
|
@ -68,14 +68,14 @@ iotest!(fn test_destroy_once() {
|
|||
|
||||
#[cfg(unix)]
|
||||
pub fn sleeper() -> Process {
|
||||
Process::new("sleep", ["1000".to_owned()]).unwrap()
|
||||
Command::new("sleep").arg("1000").spawn().unwrap()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
pub fn sleeper() -> Process {
|
||||
// There's a `timeout` command on windows, but it doesn't like having
|
||||
// its output piped, so instead just ping ourselves a few times with
|
||||
// gaps inbetweeen so we're sure this process is alive for awhile
|
||||
Process::new("ping", ["127.0.0.1".to_owned(), "-n".to_owned(), "1000".to_owned()]).unwrap()
|
||||
Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap()
|
||||
}
|
||||
|
||||
iotest!(fn test_destroy_twice() {
|
||||
|
|
@ -85,7 +85,7 @@ iotest!(fn test_destroy_twice() {
|
|||
})
|
||||
|
||||
pub fn test_destroy_actually_kills(force: bool) {
|
||||
use std::io::process::{Process, ProcessOutput, ExitStatus, ExitSignal};
|
||||
use std::io::process::{Command, ProcessOutput, ExitStatus, ExitSignal};
|
||||
use std::io::timer;
|
||||
use libc;
|
||||
use std::str;
|
||||
|
|
@ -100,7 +100,7 @@ pub fn test_destroy_actually_kills(force: bool) {
|
|||
static BLOCK_COMMAND: &'static str = "cmd";
|
||||
|
||||
// this process will stay alive indefinitely trying to read from stdin
|
||||
let mut p = Process::new(BLOCK_COMMAND, []).unwrap();
|
||||
let mut p = Command::new(BLOCK_COMMAND).spawn().unwrap();
|
||||
|
||||
assert!(p.signal(0).is_ok());
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use std::io::process;
|
|||
pub fn main () {
|
||||
let args = os::args();
|
||||
let args = args.as_slice();
|
||||
if args.len() > 1 && args[1] == "child".to_owned() {
|
||||
if args.len() > 1 && args[1].as_slice() == "child" {
|
||||
for _ in range(0, 1000) {
|
||||
println!("hello?");
|
||||
}
|
||||
|
|
@ -28,14 +28,7 @@ pub fn main () {
|
|||
return;
|
||||
}
|
||||
|
||||
let config = process::ProcessConfig {
|
||||
program : args[0].as_slice(),
|
||||
args : &["child".to_owned()],
|
||||
stdout: process::Ignored,
|
||||
stderr: process::Ignored,
|
||||
.. process::ProcessConfig::new()
|
||||
};
|
||||
|
||||
let mut p = process::Process::configure(config).unwrap();
|
||||
println!("{}", p.wait());
|
||||
let mut p = process::Command::new(args[0].as_slice());
|
||||
p.arg("child").stdout(process::Ignored).stderr(process::Ignored);
|
||||
println!("{}", p.spawn().unwrap().wait());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ extern crate rustuv;
|
|||
extern crate libc;
|
||||
|
||||
use std::io::process;
|
||||
use std::io::process::Command;
|
||||
use std::io::signal::{Listener, Interrupt};
|
||||
|
||||
#[start]
|
||||
|
|
@ -34,19 +35,12 @@ fn start(argc: int, argv: **u8) -> int {
|
|||
fn main() {
|
||||
unsafe { libc::setsid(); }
|
||||
|
||||
let config = process::ProcessConfig {
|
||||
program : "/bin/sh",
|
||||
args: &["-c".to_owned(), "read a".to_owned()],
|
||||
detach: true,
|
||||
.. process::ProcessConfig::new()
|
||||
};
|
||||
|
||||
// we shouldn't die because of an interrupt
|
||||
let mut l = Listener::new();
|
||||
l.register(Interrupt).unwrap();
|
||||
|
||||
// spawn the child
|
||||
let mut p = process::Process::configure(config).unwrap();
|
||||
let mut p = Command::new("/bin/sh").arg("-c").arg("read a").detached().spawn().unwrap();
|
||||
|
||||
// send an interrupt to everyone in our process group
|
||||
unsafe { libc::funcs::posix88::signal::kill(0, libc::SIGINT); }
|
||||
|
|
@ -59,4 +53,3 @@ fn main() {
|
|||
process::ExitSignal(..) => fail!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ extern crate native;
|
|||
|
||||
use std::io;
|
||||
use std::io::fs;
|
||||
use std::io::process::Process;
|
||||
use std::io::process::ProcessConfig;
|
||||
use std::io::Command;
|
||||
use std::os;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -56,13 +55,11 @@ fn main() {
|
|||
assert!(fs::copy(&my_path, &child_path).is_ok());
|
||||
|
||||
// run child
|
||||
let p = Process::configure(ProcessConfig {
|
||||
program: child_path.as_str().unwrap(),
|
||||
args: [arg.to_owned()],
|
||||
cwd: Some(&cwd),
|
||||
env: Some(my_env.append_one(env).as_slice()),
|
||||
.. ProcessConfig::new()
|
||||
}).unwrap().wait_with_output().unwrap();
|
||||
let p = Command::new(&child_path)
|
||||
.arg(arg)
|
||||
.cwd(&cwd)
|
||||
.env(my_env.append_one(env).as_slice())
|
||||
.spawn().unwrap().wait_with_output().unwrap();
|
||||
|
||||
// display the output
|
||||
assert!(io::stdout().write(p.output.as_slice()).is_ok());
|
||||
|
|
|
|||
|
|
@ -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() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue