auto merge of #10936 : cadencemarseille/rust/issue-10754-std-run-unwrap-on-None, r=alexcrichton

The problem was that std::run::Process::new() was unwrap()ing the result
of std::io::process::Process::new(), which returns None in the case
where the io_error condition is raised to signal failure to start the
process.

Have std::run::Process::new() similarly return an Option\<run::Process\>
to reflect the fact that a subprocess might have failed to start. Update
utility functions run::process_status() and run::process_output() to
return Option\<ProcessExit\> and Option\<ProcessOutput\>, respectively.

Various parts of librustc and librustpkg needed to be updated to reflect
these API changes.

closes #10754
This commit is contained in:
bors 2013-12-14 12:56:22 -08:00
commit aafed3ece5
12 changed files with 274 additions and 152 deletions

View File

@ -46,10 +46,10 @@ pub fn run(lib_path: &str,
prog: &str,
args: &[~str],
env: ~[(~str, ~str)],
input: Option<~str>) -> Result {
input: Option<~str>) -> Option<Result> {
let env = env + target_env(lib_path, prog);
let mut process = run::Process::new(prog, args, run::ProcessOptions {
let mut opt_process = run::Process::new(prog, args, run::ProcessOptions {
env: Some(env),
dir: None,
in_fd: None,
@ -57,15 +57,20 @@ pub fn run(lib_path: &str,
err_fd: None
});
for input in input.iter() {
process.input().write(input.as_bytes());
}
let run::ProcessOutput { status, output, error } = process.finish_with_output();
match opt_process {
Some(ref mut process) => {
for input in input.iter() {
process.input().write(input.as_bytes());
}
let run::ProcessOutput { status, output, error } = process.finish_with_output();
Result {
status: status,
out: str::from_utf8_owned(output),
err: str::from_utf8_owned(error)
Some(Result {
status: status,
out: str::from_utf8_owned(output),
err: str::from_utf8_owned(error)
})
},
None => None
}
}
@ -73,10 +78,10 @@ pub fn run_background(lib_path: &str,
prog: &str,
args: &[~str],
env: ~[(~str, ~str)],
input: Option<~str>) -> run::Process {
input: Option<~str>) -> Option<run::Process> {
let env = env + target_env(lib_path, prog);
let mut process = run::Process::new(prog, args, run::ProcessOptions {
let opt_process = run::Process::new(prog, args, run::ProcessOptions {
env: Some(env),
dir: None,
in_fd: None,
@ -84,9 +89,14 @@ pub fn run_background(lib_path: &str,
err_fd: None
});
for input in input.iter() {
process.input().write(input.as_bytes());
}
match opt_process {
Some(mut process) => {
for input in input.iter() {
process.input().write(input.as_bytes());
}
return process;
Some(process)
},
None => None
}
}

View File

@ -289,20 +289,23 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
dump_output_file(config, testfile, script_str, "debugger.script");
procsrv::run("", config.adb_path.clone(),
procsrv::run("", config.adb_path,
[~"push", exe_file.as_str().unwrap().to_owned(), config.adb_test_dir.clone()],
~[(~"",~"")], Some(~""));
~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
procsrv::run("", config.adb_path,
[~"forward", ~"tcp:5039", ~"tcp:5039"],
~[(~"",~"")], Some(~""));
~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
let adb_arg = format!("export LD_LIBRARY_PATH={}; gdbserver :5039 {}/{}",
config.adb_test_dir.clone(), config.adb_test_dir.clone(),
str::from_utf8(exe_file.filename().unwrap()));
let mut process = procsrv::run_background("", config.adb_path.clone(),
[~"shell",adb_arg.clone()],~[(~"",~"")], Some(~""));
let mut process = procsrv::run_background("", config.adb_path,
[~"shell",adb_arg.clone()],~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
loop {
//waiting 1 second for gdbserver start
timer::sleep(1000);
@ -334,10 +337,12 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
let debugger_opts = ~[~"-quiet", ~"-batch", ~"-nx",
"-command=" + debugger_script.as_str().unwrap().to_owned()];
let gdb_path = tool_path.append("/bin/arm-linux-androideabi-gdb");
let procsrv::Result{ out, err, status }=
procsrv::run("",
tool_path.append("/bin/arm-linux-androideabi-gdb"),
debugger_opts, ~[(~"",~"")], None);
gdb_path,
debugger_opts, ~[(~"",~"")], None)
.expect(format!("failed to exec `{}`", gdb_path));
let cmdline = {
let cmdline = make_cmdline("", "arm-linux-androideabi-gdb", debugger_opts);
logv(config, format!("executing {}", cmdline));
@ -800,7 +805,8 @@ fn program_output(config: &config, testfile: &Path, lib_path: &str, prog: ~str,
cmdline
};
let procsrv::Result{ out, err, status } =
procsrv::run(lib_path, prog, args, env, input);
procsrv::run(lib_path, prog, args, env, input)
.expect(format!("failed to exec `{}`", prog));
dump_output(config, testfile, out, err);
return ProcRes {status: status,
stdout: out,
@ -908,7 +914,8 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps,
// copy to target
let copy_result = procsrv::run("", config.adb_path,
[~"push", args.prog.clone(), config.adb_test_dir.clone()],
~[(~"",~"")], Some(~""));
~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
if config.verbose {
println!("push ({}) {} {} {}",
@ -932,7 +939,8 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps,
for tv in args.args.iter() {
runargs.push(tv.to_owned());
}
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""));
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
// get exitcode of result
runargs = ~[];
@ -942,7 +950,8 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps,
let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")],
Some(~""));
Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
let mut exitcode : int = 0;
for c in exitcode_out.chars() {
@ -960,7 +969,8 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps,
runargs.push(format!("{}/{}.stdout", config.adb_test_dir, prog_short));
let procsrv::Result{ out: stdout_out, err: _, status: _ } =
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""));
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
// get stderr of result
runargs = ~[];
@ -969,7 +979,8 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps,
runargs.push(format!("{}/{}.stderr", config.adb_test_dir, prog_short));
let procsrv::Result{ out: stderr_out, err: _, status: _ } =
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""));
procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
dump_output(config, testfile, stdout_out, stderr_out);
@ -1004,7 +1015,8 @@ fn _arm_push_aux_shared_library(config: &config, testfile: &Path) {
// FIXME (#9639): This needs to handle non-utf8 paths
let copy_result = procsrv::run("", config.adb_path,
[~"push", file.as_str().unwrap().to_owned(), config.adb_test_dir.clone()],
~[(~"",~"")], Some(~""));
~[(~"",~"")], Some(~""))
.expect(format!("failed to exec `{}`", config.adb_path));
if config.verbose {
println!("push ({}) {} {} {}",

View File

@ -40,15 +40,25 @@ fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
Some(p) => { debug!("inside {}", p.display()); }
None => {}
}
let o = Process::new(ar, args.as_slice(), opts).finish_with_output();
if !o.status.success() {
sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
o.status));
sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
sess.abort_if_errors();
let mut opt_prog = Process::new(ar, args.as_slice(), opts);
match opt_prog {
Some(ref mut prog) => {
let o = prog.finish_with_output();
if !o.status.success() {
sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
o.status));
sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
sess.abort_if_errors();
}
o
},
None => {
sess.err(format!("could not exec `{}`", ar));
sess.abort_if_errors();
fail!("rustc::back::archive::run_ar() should not reach this point");
}
}
o
}
impl Archive {

View File

@ -310,13 +310,19 @@ pub mod write {
assembly.as_str().unwrap().to_owned()];
debug!("{} '{}'", cc, args.connect("' '"));
let prog = run::process_output(cc, args);
if !prog.status.success() {
sess.err(format!("linking with `{}` failed: {}", cc, prog.status));
sess.note(format!("{} arguments: '{}'", cc, args.connect("' '")));
sess.note(str::from_utf8_owned(prog.error + prog.output));
sess.abort_if_errors();
match run::process_output(cc, args) {
Some(prog) => {
if !prog.status.success() {
sess.err(format!("linking with `{}` failed: {}", cc, prog.status));
sess.note(format!("{} arguments: '{}'", cc, args.connect("' '")));
sess.note(str::from_utf8_owned(prog.error + prog.output));
sess.abort_if_errors();
}
},
None => {
sess.err(format!("could not exec `{}`", cc));
sess.abort_if_errors();
}
}
}
@ -949,14 +955,22 @@ fn link_natively(sess: Session, dylib: bool, obj_filename: &Path,
// Invoke the system linker
debug!("{} {}", cc_prog, cc_args.connect(" "));
let prog = time(sess.time_passes(), "running linker", (), |()|
run::process_output(cc_prog, cc_args));
let opt_prog = time(sess.time_passes(), "running linker", (), |()|
run::process_output(cc_prog, cc_args));
if !prog.status.success() {
sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
sess.note(format!("{} arguments: '{}'", cc_prog, cc_args.connect("' '")));
sess.note(str::from_utf8_owned(prog.error + prog.output));
sess.abort_if_errors();
match opt_prog {
Some(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.note(str::from_utf8_owned(prog.error + prog.output));
sess.abort_if_errors();
}
},
None => {
sess.err(format!("could not exec `{}`", cc_prog));
sess.abort_if_errors();
}
}

View File

@ -142,14 +142,14 @@ pub fn install_pkg(cx: &BuildContext,
/// Builds an arbitrary library whose short name is `output`,
/// by invoking `tool` with arguments `args` plus "-o %s", where %s
/// is the platform-specific library name for `output`.
/// Returns that platform-specific name.
/// Returns that platform-specific name, or None if `tool` could not be started.
pub fn build_library_in_workspace(exec: &mut workcache::Exec,
context: &mut Context,
package_name: &str,
tool: &str,
flags: &[~str],
paths: &[~str],
output: &str) -> ~str {
output: &str) -> Option<~str> {
use command_failed = conditions::command_failed::cond;
let workspace = my_workspace(context, package_name);
@ -169,16 +169,20 @@ pub fn build_library_in_workspace(exec: &mut workcache::Exec,
let all_args = flags + absolute_paths + cc_args +
~[~"-o", out_name.as_str().unwrap().to_owned()];
let exit_process = run::process_status(tool, all_args);
if exit_process.success() {
let out_name_str = out_name.as_str().unwrap().to_owned();
exec.discover_output("binary",
out_name_str,
digest_only_date(&out_name));
context.add_library_path(out_name.dir_path());
out_name_str
} else {
command_failed.raise((tool.to_owned(), all_args, exit_process))
match run::process_status(tool, all_args) {
Some(exit_process) => {
if exit_process.success() {
let out_name_str = out_name.as_str().unwrap().to_owned();
exec.discover_output("binary",
out_name_str,
digest_only_date(&out_name));
context.add_library_path(out_name.dir_path());
Some(out_name_str)
} else {
Some(command_failed.raise((tool.to_owned(), all_args, exit_process)))
}
},
None => None
}
}

View File

@ -166,29 +166,47 @@ impl<'a> PkgScript<'a> {
/// Run the contents of this package script, where <what>
/// is the command to pass to it (e.g., "build", "clean", "install")
/// Returns a pair of an exit code and list of configs (obtained by
/// calling the package script's configs() function if it exists
fn run_custom(exe: &Path, sysroot: &Path) -> (~[~str], process::ProcessExit) {
/// calling the package script's configs() function if it exists, or
/// None if `exe` could not be started.
fn run_custom(exe: &Path, sysroot: &Path) -> Option<(~[~str], process::ProcessExit)> {
debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(),
sysroot.display(), "install");
// FIXME #7401 should support commands besides `install`
// FIXME (#9639): This needs to handle non-utf8 paths
let status = run::process_status(exe.as_str().unwrap(),
[sysroot.as_str().unwrap().to_owned(), ~"install"]);
if !status.success() {
debug!("run_custom: first pkg command failed with {:?}", status);
(~[], status)
}
else {
debug!("Running program (configs): {} {} {}",
exe.display(), sysroot.display(), "configs");
// FIXME (#9639): This needs to handle non-utf8 paths
let output = run::process_output(exe.as_str().unwrap(),
[sysroot.as_str().unwrap().to_owned(), ~"configs"]);
debug!("run_custom: second pkg command did {:?}", output.status);
// Run the configs() function to get the configs
let cfgs = str::from_utf8(output.output).words()
.map(|w| w.to_owned()).collect();
(cfgs, output.status)
let opt_status = run::process_status(exe.as_str().unwrap(),
[sysroot.as_str().unwrap().to_owned(), ~"install"]);
match opt_status {
Some(status) => {
if !status.success() {
debug!("run_custom: first pkg command failed with {:?}", status);
Some((~[], status))
}
else {
debug!("Running program (configs): {} {} {}",
exe.display(), sysroot.display(), "configs");
// FIXME (#9639): This needs to handle non-utf8 paths
let opt_output = run::process_output(exe.as_str().unwrap(),
[sysroot.as_str().unwrap().to_owned(),
~"configs"]);
match opt_output {
Some(output) => {
debug!("run_custom: second pkg command did {:?}", output.status);
// Run the configs() function to get the configs
let cfgs = str::from_utf8(output.output).words()
.map(|w| w.to_owned()).collect();
Some((cfgs, output.status))
},
None => {
debug!("run_custom: second pkg command failed to start");
Some((~[], status))
}
}
}
},
None => {
debug!("run_custom: first pkg command failed to start");
None
}
}
}
}
@ -481,14 +499,20 @@ impl CtxMethods for BuildContext {
})
});
// We always *run* the package script
let (cfgs, hook_result) = PkgScript::run_custom(&Path::new(pkg_exe), &sysroot);
debug!("Command return code = {:?}", hook_result);
if !hook_result.success() {
fail!("Error running custom build command")
match PkgScript::run_custom(&Path::new(pkg_exe), &sysroot) {
Some((cfgs, hook_result)) => {
debug!("Command return code = {:?}", hook_result);
if !hook_result.success() {
fail!("Error running custom build command")
}
custom = true;
// otherwise, the package script succeeded
cfgs
},
None => {
fail!("Error starting custom build command")
}
}
custom = true;
// otherwise, the package script succeeded
cfgs
}
(Some(_), Inferred) => {
debug!("There is a package script, but we're ignoring it");
@ -693,9 +717,14 @@ impl CtxMethods for BuildContext {
Some(test_exec) => {
debug!("test: test_exec = {}", test_exec.display());
// FIXME (#9639): This needs to handle non-utf8 paths
let status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]);
if !status.success() {
fail!("Some tests failed");
let opt_status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]);
match opt_status {
Some(status) => {
if !status.success() {
fail!("Some tests failed");
}
},
None => fail!("Could not exec `{}`", test_exec.display())
}
}
None => {

View File

@ -33,15 +33,16 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
if !target.exists() {
debug!("Running: git clone {} {}", source.display(), target.display());
// FIXME (#9639): This needs to handle non-utf8 paths
let outp = run::process_output("git", [~"clone",
source.as_str().unwrap().to_owned(),
target.as_str().unwrap().to_owned()]);
let opt_outp = run::process_output("git", [~"clone",
source.as_str().unwrap().to_owned(),
target.as_str().unwrap().to_owned()]);
let outp = opt_outp.expect("Failed to exec `git`");
if !outp.status.success() {
println(str::from_utf8_owned(outp.output.clone()));
println(str::from_utf8_owned(outp.error));
return DirToUse(target.clone());
}
else {
else {
match v {
&ExactRevision(ref s) => {
let git_dir = target.join(".git");
@ -51,7 +52,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
let outp = run::process_output("git",
[format!("--work-tree={}", target.as_str().unwrap().to_owned()),
format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()),
~"checkout", format!("{}", *s)]);
~"checkout", format!("{}", *s)]).expect("Failed to exec `git`");
if !outp.status.success() {
println(str::from_utf8_owned(outp.output.clone()));
println(str::from_utf8_owned(outp.error));
@ -72,7 +73,8 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
let args = [format!("--work-tree={}", target.as_str().unwrap().to_owned()),
format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()),
~"pull", ~"--no-edit", source.as_str().unwrap().to_owned()];
let outp = run::process_output("git", args);
let opt_outp = run::process_output("git", args);
let outp = opt_outp.expect("Failed to exec `git`");
assert!(outp.status.success());
}
CheckedOutSources
@ -108,8 +110,9 @@ pub fn git_clone_url(source: &str, target: &Path, v: &Version) {
use conditions::git_checkout_failed::cond;
// FIXME (#9639): This needs to handle non-utf8 paths
let outp = run::process_output("git", [~"clone", source.to_owned(),
target.as_str().unwrap().to_owned()]);
let opt_outp = run::process_output("git", [~"clone", source.to_owned(),
target.as_str().unwrap().to_owned()]);
let outp = opt_outp.expect("Failed to exec `git`");
if !outp.status.success() {
debug!("{}", str::from_utf8_owned(outp.output.clone()));
debug!("{}", str::from_utf8_owned(outp.error));
@ -118,8 +121,9 @@ pub fn git_clone_url(source: &str, target: &Path, v: &Version) {
else {
match v {
&ExactRevision(ref s) | &Tagged(ref s) => {
let outp = process_output_in_cwd("git", [~"checkout", s.to_owned()],
let opt_outp = process_output_in_cwd("git", [~"checkout", s.to_owned()],
target);
let outp = opt_outp.expect("Failed to exec `git`");
if !outp.status.success() {
debug!("{}", str::from_utf8_owned(outp.output.clone()));
debug!("{}", str::from_utf8_owned(outp.error));
@ -131,10 +135,13 @@ pub fn git_clone_url(source: &str, target: &Path, v: &Version) {
}
}
fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> ProcessOutput {
let mut prog = Process::new(prog, args, ProcessOptions{ dir: Some(cwd)
,..ProcessOptions::new()});
prog.finish_with_output()
fn process_output_in_cwd(prog: &str, args: &[~str], cwd: &Path) -> Option<ProcessOutput> {
let mut opt_prog = Process::new(prog, args, ProcessOptions{ dir: Some(cwd)
,..ProcessOptions::new()});
match opt_prog {
Some(ref mut prog) => Some(prog.finish_with_output()),
None => None
}
}
pub fn is_git_dir(p: &Path) -> bool {

View File

@ -148,7 +148,7 @@ fn run_git(args: &[~str], env: Option<~[(~str, ~str)]>, cwd: &Path, err_msg: &st
in_fd: None,
out_fd: None,
err_fd: None
});
}).expect("failed to exec `git`");
let rslt = prog.finish_with_output();
if !rslt.status.success() {
fail!("{} [git returned {:?}, output = {}, error = {}]", err_msg,
@ -285,7 +285,7 @@ fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~s
in_fd: None,
out_fd: None,
err_fd: None
});
}).expect(format!("failed to exec `{}`", cmd));
let output = prog.finish_with_output();
debug!("Output from command {} with args {:?} was {} \\{{}\\}[{:?}]",
cmd, args, str::from_utf8(output.output),
@ -503,7 +503,8 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
// n.b. Bumps time up by 2 seconds to get around granularity issues
if !run::process_output("touch", [~"--date",
~"+2 seconds",
p.as_str().unwrap().to_owned()]).status.success() {
p.as_str().unwrap().to_owned()])
.expect("failed to exec `touch`").status.success() {
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
}
}
@ -521,7 +522,8 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
// FIXME (#9639): This needs to handle non-utf8 paths
// n.b. Bumps time up by 2 seconds to get around granularity issues
if !run::process_output("touch", [~"-A02",
p.as_str().unwrap().to_owned()]).status.success() {
p.as_str().unwrap().to_owned()])
.expect("failed to exec `touch`").status.success() {
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
}
}
@ -1276,7 +1278,7 @@ fn test_extern_mod() {
in_fd: None,
out_fd: None,
err_fd: None
});
}).expect(format!("failed to exec `{}`", rustc.as_str().unwrap()));
let outp = prog.finish_with_output();
if !outp.status.success() {
fail!("output was {}, error was {}",
@ -1331,7 +1333,7 @@ fn test_extern_mod_simpler() {
in_fd: None,
out_fd: None,
err_fd: None
});
}).expect(format!("failed to exec `{}`", rustc.as_str().unwrap()));
let outp = prog.finish_with_output();
if !outp.status.success() {
fail!("output was {}, error was {}",

View File

@ -104,8 +104,9 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
continue;
}
// FIXME (#9639): This needs to handle non-utf8 paths
let outp = run::process_output("git",
let opt_outp = run::process_output("git",
["--git-dir=" + git_dir.as_str().unwrap(), ~"tag", ~"-l"]);
let outp = opt_outp.expect("Failed to exec `git`");
debug!("git --git-dir={} tag -l ~~~> {:?}", git_dir.display(), outp.status);
@ -140,9 +141,10 @@ pub fn try_getting_version(remote_path: &Path) -> Option<Version> {
remote_path.display(),
tmp_dir.display());
// FIXME (#9639): This needs to handle non-utf8 paths
let outp = run::process_output("git", [~"clone", format!("https://{}",
remote_path.as_str().unwrap()),
tmp_dir.as_str().unwrap().to_owned()]);
let opt_outp = run::process_output("git", [~"clone", format!("https://{}",
remote_path.as_str().unwrap()),
tmp_dir.as_str().unwrap().to_owned()]);
let outp = opt_outp.expect("Failed to exec `git`");
if outp.status.success() {
debug!("Cloned it... ( {}, {} )",
str::from_utf8(outp.output),
@ -152,9 +154,10 @@ pub fn try_getting_version(remote_path: &Path) -> Option<Version> {
debug!("(getting version, now getting tags) executing \\{git --git-dir={} tag -l\\}",
git_dir.display());
// FIXME (#9639): This needs to handle non-utf8 paths
let outp = run::process_output("git",
["--git-dir=" + git_dir.as_str().unwrap(),
~"tag", ~"-l"]);
let opt_outp = run::process_output("git",
["--git-dir=" + git_dir.as_str().unwrap(),
~"tag", ~"-l"]);
let outp = opt_outp.expect("Failed to exec `git`");
let output_text = str::from_utf8(outp.output);
debug!("Full output: ( {} ) [{:?}]", output_text, outp.status);
for l in output_text.lines() {

View File

@ -119,7 +119,7 @@ impl Process {
* * options - Options to configure the environment of the process,
* the working directory and the standard IO streams.
*/
pub fn new(prog: &str, args: &[~str], options: ProcessOptions) -> Process {
pub fn new(prog: &str, args: &[~str], options: ProcessOptions) -> Option<Process> {
let ProcessOptions { env, dir, in_fd, out_fd, err_fd } = options;
let env = env.as_ref().map(|a| a.as_slice());
let cwd = dir.as_ref().map(|a| a.as_str().unwrap());
@ -138,8 +138,10 @@ impl Process {
cwd: cwd,
io: rtio,
};
let inner = process::Process::new(rtconfig).unwrap();
Process { inner: inner }
match process::Process::new(rtconfig) {
Some(inner) => Some(Process { inner: inner }),
None => None
}
}
/// Returns the unique id of the process
@ -290,17 +292,20 @@ impl Process {
*
* # Return value
*
* The process's exit code
* The process's exit code, or None if the child process could not be started
*/
pub fn process_status(prog: &str, args: &[~str]) -> ProcessExit {
let mut prog = Process::new(prog, args, ProcessOptions {
pub fn process_status(prog: &str, args: &[~str]) -> Option<ProcessExit> {
let mut opt_prog = Process::new(prog, args, ProcessOptions {
env: None,
dir: None,
in_fd: Some(unsafe { libc::dup(libc::STDIN_FILENO) }),
out_fd: Some(unsafe { libc::dup(libc::STDOUT_FILENO) }),
err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) })
});
prog.finish()
match opt_prog {
Some(ref mut prog) => Some(prog.finish()),
None => None
}
}
/**
@ -313,11 +318,15 @@ pub fn process_status(prog: &str, args: &[~str]) -> ProcessExit {
*
* # Return value
*
* The process's stdout/stderr output and exit code.
* The process's stdout/stderr output and exit code, or None if the child process could not be
* started.
*/
pub fn process_output(prog: &str, args: &[~str]) -> ProcessOutput {
let mut prog = Process::new(prog, args, ProcessOptions::new());
prog.finish_with_output()
pub fn process_output(prog: &str, args: &[~str]) -> Option<ProcessOutput> {
let mut opt_prog = Process::new(prog, args, ProcessOptions::new());
match opt_prog {
Some(ref mut prog) => Some(prog.finish_with_output()),
None => None
}
}
#[cfg(test)]
@ -331,24 +340,36 @@ mod tests {
use task::spawn;
use unstable::running_on_valgrind;
use io::native::file;
use io::{Writer, Reader};
use io::{Writer, Reader, io_error};
#[test]
#[cfg(not(target_os="android"))] // FIXME(#10380)
fn test_process_status() {
let mut status = run::process_status("false", []);
let mut status = run::process_status("false", []).expect("failed to exec `false`");
assert!(status.matches_exit_status(1));
status = run::process_status("true", []);
status = run::process_status("true", []).expect("failed to exec `true`");
assert!(status.success());
}
#[test]
fn test_process_output_fail_to_start() {
let mut trapped_io_error = false;
let opt_outp = io_error::cond.trap(|_| {
trapped_io_error = true;
}).inside(|| -> Option<run::ProcessOutput> {
run::process_output("no-binary-by-this-name-should-exist", [])
});
assert!(trapped_io_error);
assert!(opt_outp.is_none());
}
#[test]
#[cfg(not(target_os="android"))] // FIXME(#10380)
fn test_process_output_output() {
let run::ProcessOutput {status, output, error}
= run::process_output("echo", [~"hello"]);
= run::process_output("echo", [~"hello"]).expect("failed to exec `echo`");
let output_str = str::from_utf8_owned(output);
assert!(status.success());
@ -364,7 +385,7 @@ mod tests {
fn test_process_output_error() {
let run::ProcessOutput {status, output, error}
= run::process_output("mkdir", [~"."]);
= run::process_output("mkdir", [~"."]).expect("failed to exec `mkdir`");
assert!(status.matches_exit_status(1));
assert_eq!(output, ~[]);
@ -385,7 +406,7 @@ mod tests {
in_fd: Some(pipe_in.input),
out_fd: Some(pipe_out.out),
err_fd: Some(pipe_err.out)
});
}).expect("failed to exec `cat`");
os::close(pipe_in.input);
os::close(pipe_out.out);
@ -422,14 +443,16 @@ mod tests {
#[test]
#[cfg(not(target_os="android"))] // FIXME(#10380)
fn test_finish_once() {
let mut prog = run::Process::new("false", [], run::ProcessOptions::new());
let mut prog = run::Process::new("false", [], run::ProcessOptions::new())
.expect("failed to exec `false`");
assert!(prog.finish().matches_exit_status(1));
}
#[test]
#[cfg(not(target_os="android"))] // FIXME(#10380)
fn test_finish_twice() {
let mut prog = run::Process::new("false", [], run::ProcessOptions::new());
let mut prog = run::Process::new("false", [], run::ProcessOptions::new())
.expect("failed to exec `false`");
assert!(prog.finish().matches_exit_status(1));
assert!(prog.finish().matches_exit_status(1));
}
@ -438,7 +461,8 @@ mod tests {
#[cfg(not(target_os="android"))] // FIXME(#10380)
fn test_finish_with_output_once() {
let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions::new());
let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions::new())
.expect("failed to exec `echo`");
let run::ProcessOutput {status, output, error}
= prog.finish_with_output();
let output_str = str::from_utf8_owned(output);
@ -455,7 +479,8 @@ mod tests {
#[cfg(not(target_os="android"))] // FIXME(#10380)
fn test_finish_with_output_twice() {
let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions::new());
let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions::new())
.expect("failed to exec `echo`");
let run::ProcessOutput {status, output, error}
= prog.finish_with_output();
@ -484,14 +509,14 @@ mod tests {
run::Process::new("pwd", [], run::ProcessOptions {
dir: dir,
.. run::ProcessOptions::new()
})
}).expect("failed to exec `pwd`")
}
#[cfg(unix,target_os="android")]
fn run_pwd(dir: Option<&Path>) -> run::Process {
run::Process::new("/system/bin/sh", [~"-c",~"pwd"], run::ProcessOptions {
dir: dir,
.. run::ProcessOptions::new()
})
}).expect("failed to exec `/system/bin/sh`")
}
#[cfg(windows)]
@ -499,7 +524,7 @@ mod tests {
run::Process::new("cmd", [~"/c", ~"cd"], run::ProcessOptions {
dir: dir,
.. run::ProcessOptions::new()
})
}).expect("failed to run `cmd`")
}
#[test]
@ -539,14 +564,14 @@ mod tests {
run::Process::new("env", [], run::ProcessOptions {
env: env,
.. run::ProcessOptions::new()
})
}).expect("failed to exec `env`")
}
#[cfg(unix,target_os="android")]
fn run_env(env: Option<~[(~str, ~str)]>) -> run::Process {
run::Process::new("/system/bin/sh", [~"-c",~"set"], run::ProcessOptions {
env: env,
.. run::ProcessOptions::new()
})
}).expect("failed to exec `/system/bin/sh`")
}
#[cfg(windows)]
@ -554,7 +579,7 @@ mod tests {
run::Process::new("cmd", [~"/c", ~"set"], run::ProcessOptions {
env: env,
.. run::ProcessOptions::new()
})
}).expect("failed to run `cmd`")
}
#[test]

View File

@ -27,7 +27,8 @@ fn test_destroy_once() {
#[cfg(target_os="android")]
static PROG: &'static str = "ls"; // android don't have echo binary
let mut p = run::Process::new(PROG, [], run::ProcessOptions::new());
let mut p = run::Process::new(PROG, [], run::ProcessOptions::new())
.expect(format!("failed to exec `{}`", PROG));
p.destroy(); // this shouldn't crash (and nor should the destructor)
}
@ -38,7 +39,8 @@ fn test_destroy_twice() {
#[cfg(target_os="android")]
static PROG: &'static str = "ls"; // android don't have echo binary
let mut p = run::Process::new(PROG, [], run::ProcessOptions::new());
let mut p = run::Process::new(PROG, [], run::ProcessOptions::new())
.expect(format!("failed to exec `{}`", PROG));
p.destroy(); // this shouldnt crash...
io::io_error::cond.trap(|_| {}).inside(|| {
p.destroy(); // ...and nor should this (and nor should the destructor)
@ -58,13 +60,15 @@ fn test_destroy_actually_kills(force: bool) {
#[cfg(unix,not(target_os="android"))]
fn process_exists(pid: libc::pid_t) -> bool {
let run::ProcessOutput {output, ..} = run::process_output("ps", [~"-p", pid.to_str()]);
let run::ProcessOutput {output, ..} = run::process_output("ps", [~"-p", pid.to_str()])
.expect("failed to exec `ps`");
str::from_utf8_owned(output).contains(pid.to_str())
}
#[cfg(unix,target_os="android")]
fn process_exists(pid: libc::pid_t) -> bool {
let run::ProcessOutput {output, ..} = run::process_output("/system/bin/ps", [pid.to_str()]);
let run::ProcessOutput {output, ..} = run::process_output("/system/bin/ps", [pid.to_str()])
.expect("failed to exec `/system/bin/ps`");
str::from_utf8_owned(output).contains(~"root")
}
@ -88,7 +92,8 @@ fn test_destroy_actually_kills(force: bool) {
}
// this process will stay alive indefinitely trying to read from stdin
let mut p = run::Process::new(BLOCK_COMMAND, [], run::ProcessOptions::new());
let mut p = run::Process::new(BLOCK_COMMAND, [], run::ProcessOptions::new())
.expect(format!("failed to exec `{}`", BLOCK_COMMAND));
assert!(process_exists(p.get_id()));

View File

@ -19,7 +19,8 @@ fn main() {
// Raise a segfault.
unsafe { *(0 as *mut int) = 0; }
} else {
let status = run::process_status(args[0], [~"signal"]);
let status = run::process_status(args[0], [~"signal"])
.expect("failed to exec `signal`");
// Windows does not have signal, so we get exit status 0xC0000028 (STATUS_BAD_STACK).
match status {
process::ExitSignal(_) if cfg!(unix) => {},