auto merge of #10109 : pcmattman/rust/pass-nonzero-exit-status-on-termination-by-signal, r=alexcrichton
The UvProcess exit callback is called with a zero exit status and non-zero termination signal when a child is terminated by a signal. If a parent checks only the exit status (for example, only checks the return value from `wait()`), it may believe the process completed successfully when it actually failed. Helpers for common use-cases are in `std::rt::io::process`. Should resolve https://github.com/mozilla/rust/issues/10062.
This commit is contained in:
commit
86787f8bef
15 changed files with 202 additions and 100 deletions
|
@ -11,6 +11,7 @@
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::run;
|
use std::run;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::rt::io::process::ProcessExit;
|
||||||
|
|
||||||
#[cfg(target_os = "win32")]
|
#[cfg(target_os = "win32")]
|
||||||
fn target_env(lib_path: &str, prog: &str) -> ~[(~str,~str)] {
|
fn target_env(lib_path: &str, prog: &str) -> ~[(~str,~str)] {
|
||||||
|
@ -39,7 +40,7 @@ fn target_env(_lib_path: &str, _prog: &str) -> ~[(~str,~str)] {
|
||||||
os::env()
|
os::env()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Result {status: int, out: ~str, err: ~str}
|
pub struct Result {status: ProcessExit, out: ~str, err: ~str}
|
||||||
|
|
||||||
pub fn run(lib_path: &str,
|
pub fn run(lib_path: &str,
|
||||||
prog: &str,
|
prog: &str,
|
||||||
|
|
|
@ -23,6 +23,8 @@ use util::logv;
|
||||||
use std::rt::io;
|
use std::rt::io;
|
||||||
use std::rt::io::fs;
|
use std::rt::io::fs;
|
||||||
use std::rt::io::File;
|
use std::rt::io::File;
|
||||||
|
use std::rt::io::process;
|
||||||
|
use std::rt::io::process::ProcessExit;
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
@ -60,7 +62,7 @@ pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) {
|
||||||
fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
|
fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
let ProcRes = compile_test(config, props, testfile);
|
let ProcRes = compile_test(config, props, testfile);
|
||||||
|
|
||||||
if ProcRes.status == 0 {
|
if ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"compile-fail test compiled successfully!", &ProcRes);
|
fatal_ProcRes(~"compile-fail test compiled successfully!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +83,7 @@ fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
let ProcRes = if !config.jit {
|
let ProcRes = if !config.jit {
|
||||||
let ProcRes = compile_test(config, props, testfile);
|
let ProcRes = compile_test(config, props, testfile);
|
||||||
|
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +94,7 @@ fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
|
|
||||||
// The value our Makefile configures valgrind to return on failure
|
// The value our Makefile configures valgrind to return on failure
|
||||||
static VALGRIND_ERR: int = 100;
|
static VALGRIND_ERR: int = 100;
|
||||||
if ProcRes.status == VALGRIND_ERR {
|
if ProcRes.status.matches_exit_status(VALGRIND_ERR) {
|
||||||
fatal_ProcRes(~"run-fail test isn't valgrind-clean!", &ProcRes);
|
fatal_ProcRes(~"run-fail test isn't valgrind-clean!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,10 +117,9 @@ fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
fn check_correct_failure_status(ProcRes: &ProcRes) {
|
fn check_correct_failure_status(ProcRes: &ProcRes) {
|
||||||
// The value the rust runtime returns on failure
|
// The value the rust runtime returns on failure
|
||||||
static RUST_ERR: int = 101;
|
static RUST_ERR: int = 101;
|
||||||
if ProcRes.status != RUST_ERR {
|
if !ProcRes.status.matches_exit_status(RUST_ERR) {
|
||||||
fatal_ProcRes(
|
fatal_ProcRes(
|
||||||
format!("failure produced the wrong error code: {}",
|
format!("failure produced the wrong error: {}", ProcRes.status),
|
||||||
ProcRes.status),
|
|
||||||
ProcRes);
|
ProcRes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,19 +128,19 @@ fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
if !config.jit {
|
if !config.jit {
|
||||||
let mut ProcRes = compile_test(config, props, testfile);
|
let mut ProcRes = compile_test(config, props, testfile);
|
||||||
|
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcRes = exec_compiled_test(config, props, testfile);
|
ProcRes = exec_compiled_test(config, props, testfile);
|
||||||
|
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"test run failed!", &ProcRes);
|
fatal_ProcRes(~"test run failed!", &ProcRes);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ProcRes = jit_test(config, props, testfile);
|
let ProcRes = jit_test(config, props, testfile);
|
||||||
|
|
||||||
if ProcRes.status != 0 { fatal_ProcRes(~"jit failed!", &ProcRes); }
|
if !ProcRes.status.success() { fatal_ProcRes(~"jit failed!", &ProcRes); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +161,7 @@ fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
logv(config, format!("pretty-printing round {}", round));
|
logv(config, format!("pretty-printing round {}", round));
|
||||||
let ProcRes = print_source(config, testfile, srcs[round].clone());
|
let ProcRes = print_source(config, testfile, srcs[round].clone());
|
||||||
|
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(format!("pretty-printing failed in round {}", round),
|
fatal_ProcRes(format!("pretty-printing failed in round {}", round),
|
||||||
&ProcRes);
|
&ProcRes);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +193,7 @@ fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
// Finally, let's make sure it actually appears to remain valid code
|
// Finally, let's make sure it actually appears to remain valid code
|
||||||
let ProcRes = typecheck_source(config, props, testfile, actual);
|
let ProcRes = typecheck_source(config, props, testfile, actual);
|
||||||
|
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"pretty-printed source does not typecheck", &ProcRes);
|
fatal_ProcRes(~"pretty-printed source does not typecheck", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +265,7 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
|
|
||||||
// compile test file (it shoud have 'compile-flags:-g' in the header)
|
// compile test file (it shoud have 'compile-flags:-g' in the header)
|
||||||
let mut ProcRes = compile_test(config, props, testfile);
|
let mut ProcRes = compile_test(config, props, testfile);
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +376,7 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal(~"gdb failed to execute");
|
fatal(~"gdb failed to execute");
|
||||||
}
|
}
|
||||||
let num_check_lines = check_lines.len();
|
let num_check_lines = check_lines.len();
|
||||||
|
@ -431,7 +432,7 @@ fn check_error_patterns(props: &TestProps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ProcRes.status == 0 {
|
if ProcRes.status.success() {
|
||||||
fatal(~"process did not return an error status");
|
fatal(~"process did not return an error status");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +474,7 @@ fn check_expected_errors(expected_errors: ~[errors::ExpectedError],
|
||||||
let mut found_flags = vec::from_elem(
|
let mut found_flags = vec::from_elem(
|
||||||
expected_errors.len(), false);
|
expected_errors.len(), false);
|
||||||
|
|
||||||
if ProcRes.status == 0 {
|
if ProcRes.status.success() {
|
||||||
fatal(~"process did not return an error status");
|
fatal(~"process did not return an error status");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,7 +626,7 @@ fn scan_string(haystack: &str, needle: &str, idx: &mut uint) -> bool {
|
||||||
|
|
||||||
struct ProcArgs {prog: ~str, args: ~[~str]}
|
struct ProcArgs {prog: ~str, args: ~[~str]}
|
||||||
|
|
||||||
struct ProcRes {status: int, stdout: ~str, stderr: ~str, cmdline: ~str}
|
struct ProcRes {status: ProcessExit, stdout: ~str, stderr: ~str, cmdline: ~str}
|
||||||
|
|
||||||
fn compile_test(config: &config, props: &TestProps,
|
fn compile_test(config: &config, props: &TestProps,
|
||||||
testfile: &Path) -> ProcRes {
|
testfile: &Path) -> ProcRes {
|
||||||
|
@ -692,7 +693,7 @@ fn compose_and_run_compiler(
|
||||||
|a,b| make_lib_name(a, b, testfile), &abs_ab);
|
|a,b| make_lib_name(a, b, testfile), &abs_ab);
|
||||||
let auxres = compose_and_run(config, &abs_ab, aux_args, ~[],
|
let auxres = compose_and_run(config, &abs_ab, aux_args, ~[],
|
||||||
config.compile_lib_path, None);
|
config.compile_lib_path, None);
|
||||||
if auxres.status != 0 {
|
if !auxres.status.success() {
|
||||||
fatal_ProcRes(
|
fatal_ProcRes(
|
||||||
format!("auxiliary build of {} failed to compile: ",
|
format!("auxiliary build of {} failed to compile: ",
|
||||||
abs_ab.display()),
|
abs_ab.display()),
|
||||||
|
@ -966,7 +967,12 @@ fn _arm_exec_compiled_test(config: &config, props: &TestProps,
|
||||||
|
|
||||||
dump_output(config, testfile, stdout_out, stderr_out);
|
dump_output(config, testfile, stdout_out, stderr_out);
|
||||||
|
|
||||||
ProcRes {status: exitcode, stdout: stdout_out, stderr: stderr_out, cmdline: cmdline }
|
ProcRes {
|
||||||
|
status: process::ExitStatus(exitcode),
|
||||||
|
stdout: stdout_out,
|
||||||
|
stderr: stderr_out,
|
||||||
|
cmdline: cmdline
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _dummy_exec_compiled_test(config: &config, props: &TestProps,
|
fn _dummy_exec_compiled_test(config: &config, props: &TestProps,
|
||||||
|
@ -976,9 +982,9 @@ fn _dummy_exec_compiled_test(config: &config, props: &TestProps,
|
||||||
let cmdline = make_cmdline("", args.prog, args.args);
|
let cmdline = make_cmdline("", args.prog, args.args);
|
||||||
|
|
||||||
match config.mode {
|
match config.mode {
|
||||||
mode_run_fail => ProcRes {status: 101, stdout: ~"",
|
mode_run_fail => ProcRes {status: process::ExitStatus(101), stdout: ~"",
|
||||||
stderr: ~"", cmdline: cmdline},
|
stderr: ~"", cmdline: cmdline},
|
||||||
_ => ProcRes {status: 0, stdout: ~"",
|
_ => ProcRes {status: process::ExitStatus(0), stdout: ~"",
|
||||||
stderr: ~"", cmdline: cmdline}
|
stderr: ~"", cmdline: cmdline}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1099,33 +1105,33 @@ fn run_codegen_test(config: &config, props: &TestProps,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ProcRes = compile_test_and_save_bitcode(config, props, testfile);
|
let mut ProcRes = compile_test_and_save_bitcode(config, props, testfile);
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcRes = extract_function_from_bitcode(config, props, "test", testfile, "");
|
ProcRes = extract_function_from_bitcode(config, props, "test", testfile, "");
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"extracting 'test' function failed", &ProcRes);
|
fatal_ProcRes(~"extracting 'test' function failed", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcRes = disassemble_extract(config, props, testfile, "");
|
ProcRes = disassemble_extract(config, props, testfile, "");
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"disassembling extract failed", &ProcRes);
|
fatal_ProcRes(~"disassembling extract failed", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut ProcRes = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
|
let mut ProcRes = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
fatal_ProcRes(~"compilation failed!", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcRes = extract_function_from_bitcode(config, props, "test", testfile, "clang");
|
ProcRes = extract_function_from_bitcode(config, props, "test", testfile, "clang");
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"extracting 'test' function failed", &ProcRes);
|
fatal_ProcRes(~"extracting 'test' function failed", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcRes = disassemble_extract(config, props, testfile, "clang");
|
ProcRes = disassemble_extract(config, props, testfile, "clang");
|
||||||
if ProcRes.status != 0 {
|
if !ProcRes.status.success() {
|
||||||
fatal_ProcRes(~"disassembling extract failed", &ProcRes);
|
fatal_ProcRes(~"disassembling extract failed", &ProcRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -378,9 +378,8 @@ pub mod write {
|
||||||
|
|
||||||
let prog = run::process_output(cc_prog, cc_args);
|
let prog = run::process_output(cc_prog, cc_args);
|
||||||
|
|
||||||
if prog.status != 0 {
|
if !prog.status.success() {
|
||||||
sess.err(format!("building with `{}` failed with code {}",
|
sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
|
||||||
cc_prog, prog.status));
|
|
||||||
sess.note(format!("{} arguments: {}",
|
sess.note(format!("{} arguments: {}",
|
||||||
cc_prog, cc_args.connect(" ")));
|
cc_prog, cc_args.connect(" ")));
|
||||||
sess.note(str::from_utf8(prog.error + prog.output));
|
sess.note(str::from_utf8(prog.error + prog.output));
|
||||||
|
@ -947,11 +946,11 @@ pub fn link_binary(sess: Session,
|
||||||
|
|
||||||
// We run 'cc' here
|
// We run 'cc' here
|
||||||
let prog = run::process_output(cc_prog, cc_args);
|
let prog = run::process_output(cc_prog, cc_args);
|
||||||
if 0 != prog.status {
|
|
||||||
sess.err(format!("linking with `{}` failed with code {}",
|
if !prog.status.success() {
|
||||||
cc_prog, prog.status));
|
sess.err(format!("linking with `{}` failed: {}", cc_prog, prog.status));
|
||||||
sess.note(format!("{} arguments: {}",
|
sess.note(format!("{} arguments: {}",
|
||||||
cc_prog, cc_args.connect(" ")));
|
cc_prog, cc_args.connect(" ")));
|
||||||
sess.note(str::from_utf8(prog.error + prog.output));
|
sess.note(str::from_utf8(prog.error + prog.output));
|
||||||
sess.abort_if_errors();
|
sess.abort_if_errors();
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,17 +159,16 @@ pub fn build_library_in_workspace(exec: &mut workcache::Exec,
|
||||||
|
|
||||||
let all_args = flags + absolute_paths + cc_args +
|
let all_args = flags + absolute_paths + cc_args +
|
||||||
~[~"-o", out_name.as_str().unwrap().to_owned()];
|
~[~"-o", out_name.as_str().unwrap().to_owned()];
|
||||||
let exit_code = run::process_status(tool, all_args);
|
let exit_process = run::process_status(tool, all_args);
|
||||||
if exit_code != 0 {
|
if exit_process.success() {
|
||||||
command_failed.raise((tool.to_owned(), all_args, exit_code))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let out_name_str = out_name.as_str().unwrap().to_owned();
|
let out_name_str = out_name.as_str().unwrap().to_owned();
|
||||||
exec.discover_output("binary",
|
exec.discover_output("binary",
|
||||||
out_name_str,
|
out_name_str,
|
||||||
digest_only_date(&out_name));
|
digest_only_date(&out_name));
|
||||||
context.add_library_path(out_name.dir_path());
|
context.add_library_path(out_name.dir_path());
|
||||||
out_name_str
|
out_name_str
|
||||||
|
} else {
|
||||||
|
command_failed.raise((tool.to_owned(), all_args, exit_process))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
pub use std::path::Path;
|
pub use std::path::Path;
|
||||||
pub use package_id::PkgId;
|
pub use package_id::PkgId;
|
||||||
pub use std::rt::io::FileStat;
|
pub use std::rt::io::FileStat;
|
||||||
|
pub use std::rt::io::process::ProcessExit;
|
||||||
|
|
||||||
condition! {
|
condition! {
|
||||||
pub bad_path: (Path, ~str) -> Path;
|
pub bad_path: (Path, ~str) -> Path;
|
||||||
|
@ -57,5 +58,5 @@ condition! {
|
||||||
condition! {
|
condition! {
|
||||||
// str is output of applying the command (first component)
|
// str is output of applying the command (first component)
|
||||||
// to the args (second component)
|
// to the args (second component)
|
||||||
pub command_failed: (~str, ~[~str], int) -> ~str;
|
pub command_failed: (~str, ~[~str], ProcessExit) -> ~str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ extern mod rustc;
|
||||||
extern mod syntax;
|
extern mod syntax;
|
||||||
|
|
||||||
use std::{os, result, run, str, task};
|
use std::{os, result, run, str, task};
|
||||||
|
use std::rt::io::process;
|
||||||
use std::hashmap::HashSet;
|
use std::hashmap::HashSet;
|
||||||
use std::rt::io;
|
use std::rt::io;
|
||||||
use std::rt::io::fs;
|
use std::rt::io::fs;
|
||||||
|
@ -164,14 +165,14 @@ impl<'self> PkgScript<'self> {
|
||||||
/// is the command to pass to it (e.g., "build", "clean", "install")
|
/// 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
|
/// Returns a pair of an exit code and list of configs (obtained by
|
||||||
/// calling the package script's configs() function if it exists
|
/// calling the package script's configs() function if it exists
|
||||||
fn run_custom(exe: &Path, sysroot: &Path) -> (~[~str], int) {
|
fn run_custom(exe: &Path, sysroot: &Path) -> (~[~str], process::ProcessExit) {
|
||||||
debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(),
|
debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(),
|
||||||
sysroot.display(), "install");
|
sysroot.display(), "install");
|
||||||
// FIXME #7401 should support commands besides `install`
|
// FIXME #7401 should support commands besides `install`
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||||
let status = run::process_status(exe.as_str().unwrap(),
|
let status = run::process_status(exe.as_str().unwrap(),
|
||||||
[sysroot.as_str().unwrap().to_owned(), ~"install"]);
|
[sysroot.as_str().unwrap().to_owned(), ~"install"]);
|
||||||
if status != 0 {
|
if !status.success() {
|
||||||
debug!("run_custom: first pkg command failed with {:?}", status);
|
debug!("run_custom: first pkg command failed with {:?}", status);
|
||||||
(~[], status)
|
(~[], status)
|
||||||
}
|
}
|
||||||
|
@ -486,7 +487,7 @@ impl CtxMethods for BuildContext {
|
||||||
// We always *run* the package script
|
// We always *run* the package script
|
||||||
let (cfgs, hook_result) = PkgScript::run_custom(&Path::new(pkg_exe), &sysroot);
|
let (cfgs, hook_result) = PkgScript::run_custom(&Path::new(pkg_exe), &sysroot);
|
||||||
debug!("Command return code = {:?}", hook_result);
|
debug!("Command return code = {:?}", hook_result);
|
||||||
if hook_result != 0 {
|
if !hook_result.success() {
|
||||||
fail!("Error running custom build command")
|
fail!("Error running custom build command")
|
||||||
}
|
}
|
||||||
custom = true;
|
custom = true;
|
||||||
|
@ -697,7 +698,7 @@ impl CtxMethods for BuildContext {
|
||||||
debug!("test: test_exec = {}", test_exec.display());
|
debug!("test: test_exec = {}", test_exec.display());
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||||
let status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]);
|
let status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]);
|
||||||
if status != 0 {
|
if !status.success() {
|
||||||
fail!("Some tests failed");
|
fail!("Some tests failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
|
||||||
let outp = run::process_output("git", [~"clone",
|
let outp = run::process_output("git", [~"clone",
|
||||||
source.as_str().unwrap().to_owned(),
|
source.as_str().unwrap().to_owned(),
|
||||||
target.as_str().unwrap().to_owned()]);
|
target.as_str().unwrap().to_owned()]);
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
println(str::from_utf8_owned(outp.output.clone()));
|
println(str::from_utf8_owned(outp.output.clone()));
|
||||||
println(str::from_utf8_owned(outp.error));
|
println(str::from_utf8_owned(outp.error));
|
||||||
return DirToUse(target.clone());
|
return DirToUse(target.clone());
|
||||||
|
@ -52,7 +52,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
|
||||||
[format!("--work-tree={}", target.as_str().unwrap().to_owned()),
|
[format!("--work-tree={}", target.as_str().unwrap().to_owned()),
|
||||||
format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()),
|
format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()),
|
||||||
~"checkout", format!("{}", *s)]);
|
~"checkout", format!("{}", *s)]);
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
println(str::from_utf8_owned(outp.output.clone()));
|
println(str::from_utf8_owned(outp.output.clone()));
|
||||||
println(str::from_utf8_owned(outp.error));
|
println(str::from_utf8_owned(outp.error));
|
||||||
return DirToUse(target.clone());
|
return DirToUse(target.clone());
|
||||||
|
@ -73,7 +73,7 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
|
||||||
format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()),
|
format!("--git-dir={}", git_dir.as_str().unwrap().to_owned()),
|
||||||
~"pull", ~"--no-edit", source.as_str().unwrap().to_owned()];
|
~"pull", ~"--no-edit", source.as_str().unwrap().to_owned()];
|
||||||
let outp = run::process_output("git", args);
|
let outp = run::process_output("git", args);
|
||||||
assert!(outp.status == 0);
|
assert!(outp.status.success());
|
||||||
}
|
}
|
||||||
CheckedOutSources
|
CheckedOutSources
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,7 +110,7 @@ pub fn git_clone_url(source: &str, target: &Path, v: &Version) {
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||||
let outp = run::process_output("git", [~"clone", source.to_owned(),
|
let outp = run::process_output("git", [~"clone", source.to_owned(),
|
||||||
target.as_str().unwrap().to_owned()]);
|
target.as_str().unwrap().to_owned()]);
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
debug!("{}", str::from_utf8_owned(outp.output.clone()));
|
debug!("{}", str::from_utf8_owned(outp.output.clone()));
|
||||||
debug!("{}", str::from_utf8_owned(outp.error));
|
debug!("{}", str::from_utf8_owned(outp.error));
|
||||||
cond.raise((source.to_owned(), target.clone()))
|
cond.raise((source.to_owned(), target.clone()))
|
||||||
|
@ -120,7 +120,7 @@ pub fn git_clone_url(source: &str, target: &Path, v: &Version) {
|
||||||
&ExactRevision(ref s) | &Tagged(ref s) => {
|
&ExactRevision(ref s) | &Tagged(ref s) => {
|
||||||
let outp = process_output_in_cwd("git", [~"checkout", s.to_owned()],
|
let outp = process_output_in_cwd("git", [~"checkout", s.to_owned()],
|
||||||
target);
|
target);
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
debug!("{}", str::from_utf8_owned(outp.output.clone()));
|
debug!("{}", str::from_utf8_owned(outp.output.clone()));
|
||||||
debug!("{}", str::from_utf8_owned(outp.error));
|
debug!("{}", str::from_utf8_owned(outp.error));
|
||||||
cond.raise((source.to_owned(), target.clone()))
|
cond.raise((source.to_owned(), target.clone()))
|
||||||
|
|
|
@ -15,6 +15,8 @@ use std::{os, run, str, task};
|
||||||
use std::rt::io;
|
use std::rt::io;
|
||||||
use std::rt::io::fs;
|
use std::rt::io::fs;
|
||||||
use std::rt::io::File;
|
use std::rt::io::File;
|
||||||
|
use std::rt::io::process;
|
||||||
|
use std::rt::io::process::ProcessExit;
|
||||||
use extra::arc::Arc;
|
use extra::arc::Arc;
|
||||||
use extra::arc::RWArc;
|
use extra::arc::RWArc;
|
||||||
use extra::tempfile::TempDir;
|
use extra::tempfile::TempDir;
|
||||||
|
@ -149,7 +151,7 @@ fn run_git(args: &[~str], env: Option<~[(~str, ~str)]>, cwd: &Path, err_msg: &st
|
||||||
err_fd: None
|
err_fd: None
|
||||||
});
|
});
|
||||||
let rslt = prog.finish_with_output();
|
let rslt = prog.finish_with_output();
|
||||||
if rslt.status != 0 {
|
if !rslt.status.success() {
|
||||||
fail!("{} [git returned {:?}, output = {}, error = {}]", err_msg,
|
fail!("{} [git returned {:?}, output = {}, error = {}]", err_msg,
|
||||||
rslt.status, str::from_utf8(rslt.output), str::from_utf8(rslt.error));
|
rslt.status, str::from_utf8(rslt.output), str::from_utf8(rslt.error));
|
||||||
}
|
}
|
||||||
|
@ -251,7 +253,7 @@ fn command_line_test_expect_fail(args: &[~str],
|
||||||
expected_exitcode: int) {
|
expected_exitcode: int) {
|
||||||
match command_line_test_with_env(args, cwd, env) {
|
match command_line_test_with_env(args, cwd, env) {
|
||||||
Success(_) => fail!("Should have failed with {}, but it succeeded", expected_exitcode),
|
Success(_) => fail!("Should have failed with {}, but it succeeded", expected_exitcode),
|
||||||
Fail(error) if error == expected_exitcode => (), // ok
|
Fail(error) if error.matches_exit_status(expected_exitcode) => (), // ok
|
||||||
Fail(other) => fail!("Expected to fail with {}, but failed with {} instead",
|
Fail(other) => fail!("Expected to fail with {}, but failed with {} instead",
|
||||||
expected_exitcode, other)
|
expected_exitcode, other)
|
||||||
}
|
}
|
||||||
|
@ -259,7 +261,7 @@ fn command_line_test_expect_fail(args: &[~str],
|
||||||
|
|
||||||
enum ProcessResult {
|
enum ProcessResult {
|
||||||
Success(ProcessOutput),
|
Success(ProcessOutput),
|
||||||
Fail(int) // exit code
|
Fail(ProcessExit)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs `rustpkg` (based on the directory that this executable was
|
/// Runs `rustpkg` (based on the directory that this executable was
|
||||||
|
@ -289,7 +291,7 @@ fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~s
|
||||||
cmd, args, str::from_utf8(output.output),
|
cmd, args, str::from_utf8(output.output),
|
||||||
str::from_utf8(output.error),
|
str::from_utf8(output.error),
|
||||||
output.status);
|
output.status);
|
||||||
if output.status != 0 {
|
if !output.status.success() {
|
||||||
debug!("Command {} {:?} failed with exit code {:?}; its output was --- {} ---",
|
debug!("Command {} {:?} failed with exit code {:?}; its output was --- {} ---",
|
||||||
cmd, args, output.status,
|
cmd, args, output.status,
|
||||||
str::from_utf8(output.output) + str::from_utf8(output.error));
|
str::from_utf8(output.output) + str::from_utf8(output.error));
|
||||||
|
@ -501,9 +503,9 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
|
||||||
// should be able to do this w/o a process
|
// should be able to do this w/o a process
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||||
// n.b. Bumps time up by 2 seconds to get around granularity issues
|
// n.b. Bumps time up by 2 seconds to get around granularity issues
|
||||||
if run::process_output("touch", [~"--date",
|
if !run::process_output("touch", [~"--date",
|
||||||
~"+2 seconds",
|
~"+2 seconds",
|
||||||
p.as_str().unwrap().to_owned()]).status != 0 {
|
p.as_str().unwrap().to_owned()]).status.success() {
|
||||||
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
|
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -520,8 +522,8 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) {
|
||||||
// should be able to do this w/o a process
|
// should be able to do this w/o a process
|
||||||
// FIXME (#9639): This needs to handle non-utf8 paths
|
// FIXME (#9639): This needs to handle non-utf8 paths
|
||||||
// n.b. Bumps time up by 2 seconds to get around granularity issues
|
// n.b. Bumps time up by 2 seconds to get around granularity issues
|
||||||
if run::process_output("touch", [~"-A02",
|
if !run::process_output("touch", [~"-A02",
|
||||||
p.as_str().unwrap().to_owned()]).status != 0 {
|
p.as_str().unwrap().to_owned()]).status.success() {
|
||||||
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
|
let _ = cond.raise((pkg_src_dir.clone(), ~"Bad path"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1091,7 +1093,8 @@ fn no_rebuilding() {
|
||||||
|
|
||||||
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
||||||
Success(*) => (), // ok
|
Success(*) => (), // ok
|
||||||
Fail(status) if status == 65 => fail!("no_rebuilding failed: it tried to rebuild bar"),
|
Fail(status) if status.matches_exit_status(65) =>
|
||||||
|
fail!("no_rebuilding failed: it tried to rebuild bar"),
|
||||||
Fail(_) => fail!("no_rebuilding failed for some other reason")
|
Fail(_) => fail!("no_rebuilding failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1109,7 +1112,7 @@ fn no_recopying() {
|
||||||
|
|
||||||
match command_line_test_partial([~"install", ~"foo"], workspace) {
|
match command_line_test_partial([~"install", ~"foo"], workspace) {
|
||||||
Success(*) => (), // ok
|
Success(*) => (), // ok
|
||||||
Fail(65) => fail!("no_recopying failed: it tried to re-copy foo"),
|
Fail(process::ExitStatus(65)) => fail!("no_recopying failed: it tried to re-copy foo"),
|
||||||
Fail(_) => fail!("no_copying failed for some other reason")
|
Fail(_) => fail!("no_copying failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1127,7 +1130,8 @@ fn no_rebuilding_dep() {
|
||||||
assert!(chmod_read_only(&bar_lib));
|
assert!(chmod_read_only(&bar_lib));
|
||||||
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
||||||
Success(*) => (), // ok
|
Success(*) => (), // ok
|
||||||
Fail(status) if status == 65 => fail!("no_rebuilding_dep failed: it tried to rebuild bar"),
|
Fail(status) if status.matches_exit_status(65) =>
|
||||||
|
fail!("no_rebuilding_dep failed: it tried to rebuild bar"),
|
||||||
Fail(_) => fail!("no_rebuilding_dep failed for some other reason")
|
Fail(_) => fail!("no_rebuilding_dep failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1147,7 +1151,7 @@ fn do_rebuild_dep_dates_change() {
|
||||||
|
|
||||||
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
||||||
Success(*) => fail!("do_rebuild_dep_dates_change failed: it didn't rebuild bar"),
|
Success(*) => fail!("do_rebuild_dep_dates_change failed: it didn't rebuild bar"),
|
||||||
Fail(status) if status == 65 => (), // ok
|
Fail(status) if status.matches_exit_status(65) => (), // ok
|
||||||
Fail(_) => fail!("do_rebuild_dep_dates_change failed for some other reason")
|
Fail(_) => fail!("do_rebuild_dep_dates_change failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1168,7 +1172,7 @@ fn do_rebuild_dep_only_contents_change() {
|
||||||
// should adjust the datestamp
|
// should adjust the datestamp
|
||||||
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
match command_line_test_partial([~"build", ~"foo"], workspace) {
|
||||||
Success(*) => fail!("do_rebuild_dep_only_contents_change failed: it didn't rebuild bar"),
|
Success(*) => fail!("do_rebuild_dep_only_contents_change failed: it didn't rebuild bar"),
|
||||||
Fail(status) if status == 65 => (), // ok
|
Fail(status) if status.matches_exit_status(65) => (), // ok
|
||||||
Fail(_) => fail!("do_rebuild_dep_only_contents_change failed for some other reason")
|
Fail(_) => fail!("do_rebuild_dep_only_contents_change failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1274,7 +1278,7 @@ fn test_extern_mod() {
|
||||||
err_fd: None
|
err_fd: None
|
||||||
});
|
});
|
||||||
let outp = prog.finish_with_output();
|
let outp = prog.finish_with_output();
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
fail!("output was {}, error was {}",
|
fail!("output was {}, error was {}",
|
||||||
str::from_utf8(outp.output),
|
str::from_utf8(outp.output),
|
||||||
str::from_utf8(outp.error));
|
str::from_utf8(outp.error));
|
||||||
|
@ -1329,7 +1333,7 @@ fn test_extern_mod_simpler() {
|
||||||
err_fd: None
|
err_fd: None
|
||||||
});
|
});
|
||||||
let outp = prog.finish_with_output();
|
let outp = prog.finish_with_output();
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
fail!("output was {}, error was {}",
|
fail!("output was {}, error was {}",
|
||||||
str::from_utf8(outp.output),
|
str::from_utf8(outp.output),
|
||||||
str::from_utf8(outp.error));
|
str::from_utf8(outp.error));
|
||||||
|
@ -2144,7 +2148,7 @@ fn test_rebuild_when_needed() {
|
||||||
chmod_read_only(&test_executable);
|
chmod_read_only(&test_executable);
|
||||||
match command_line_test_partial([~"test", ~"foo"], foo_workspace) {
|
match command_line_test_partial([~"test", ~"foo"], foo_workspace) {
|
||||||
Success(*) => fail!("test_rebuild_when_needed didn't rebuild"),
|
Success(*) => fail!("test_rebuild_when_needed didn't rebuild"),
|
||||||
Fail(status) if status == 65 => (), // ok
|
Fail(status) if status.matches_exit_status(65) => (), // ok
|
||||||
Fail(_) => fail!("test_rebuild_when_needed failed for some other reason")
|
Fail(_) => fail!("test_rebuild_when_needed failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2164,7 +2168,8 @@ fn test_no_rebuilding() {
|
||||||
chmod_read_only(&test_executable);
|
chmod_read_only(&test_executable);
|
||||||
match command_line_test_partial([~"test", ~"foo"], foo_workspace) {
|
match command_line_test_partial([~"test", ~"foo"], foo_workspace) {
|
||||||
Success(*) => (), // ok
|
Success(*) => (), // ok
|
||||||
Fail(status) if status == 65 => fail!("test_no_rebuilding failed: it rebuilt the tests"),
|
Fail(status) if status.matches_exit_status(65) =>
|
||||||
|
fail!("test_no_rebuilding failed: it rebuilt the tests"),
|
||||||
Fail(_) => fail!("test_no_rebuilding failed for some other reason")
|
Fail(_) => fail!("test_no_rebuilding failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2359,9 +2364,11 @@ fn test_c_dependency_no_rebuilding() {
|
||||||
|
|
||||||
match command_line_test_partial([~"build", ~"cdep"], dir) {
|
match command_line_test_partial([~"build", ~"cdep"], dir) {
|
||||||
Success(*) => (), // ok
|
Success(*) => (), // ok
|
||||||
Fail(status) if status == 65 => fail!("test_c_dependency_no_rebuilding failed: \
|
Fail(status) if status.matches_exit_status(65) =>
|
||||||
it tried to rebuild foo.c"),
|
fail!("test_c_dependency_no_rebuilding failed: \
|
||||||
Fail(_) => fail!("test_c_dependency_no_rebuilding failed for some other reason")
|
it tried to rebuild foo.c"),
|
||||||
|
Fail(_) =>
|
||||||
|
fail!("test_c_dependency_no_rebuilding failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2396,7 +2403,7 @@ fn test_c_dependency_yes_rebuilding() {
|
||||||
match command_line_test_partial([~"build", ~"cdep"], dir) {
|
match command_line_test_partial([~"build", ~"cdep"], dir) {
|
||||||
Success(*) => fail!("test_c_dependency_yes_rebuilding failed: \
|
Success(*) => fail!("test_c_dependency_yes_rebuilding failed: \
|
||||||
it didn't rebuild and should have"),
|
it didn't rebuild and should have"),
|
||||||
Fail(status) if status == 65 => (),
|
Fail(status) if status.matches_exit_status(65) => (),
|
||||||
Fail(_) => fail!("test_c_dependency_yes_rebuilding failed for some other reason")
|
Fail(_) => fail!("test_c_dependency_yes_rebuilding failed for some other reason")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ pub fn try_getting_local_version(local_path: &Path) -> Option<Version> {
|
||||||
|
|
||||||
debug!("git --git-dir={} tag -l ~~~> {:?}", git_dir.display(), outp.status);
|
debug!("git --git-dir={} tag -l ~~~> {:?}", git_dir.display(), outp.status);
|
||||||
|
|
||||||
if outp.status != 0 {
|
if !outp.status.success() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ pub fn try_getting_version(remote_path: &Path) -> Option<Version> {
|
||||||
let outp = run::process_output("git", [~"clone", format!("https://{}",
|
let outp = run::process_output("git", [~"clone", format!("https://{}",
|
||||||
remote_path.as_str().unwrap()),
|
remote_path.as_str().unwrap()),
|
||||||
tmp_dir.as_str().unwrap().to_owned()]);
|
tmp_dir.as_str().unwrap().to_owned()]);
|
||||||
if outp.status == 0 {
|
if outp.status.success() {
|
||||||
debug!("Cloned it... ( {}, {} )",
|
debug!("Cloned it... ( {}, {} )",
|
||||||
str::from_utf8(outp.output),
|
str::from_utf8(outp.output),
|
||||||
str::from_utf8(outp.error));
|
str::from_utf8(outp.error));
|
||||||
|
|
|
@ -33,8 +33,7 @@ pub struct Process {
|
||||||
to_wake: Option<BlockedTask>,
|
to_wake: Option<BlockedTask>,
|
||||||
|
|
||||||
/// Collected from the exit_cb
|
/// Collected from the exit_cb
|
||||||
exit_status: Option<int>,
|
exit_status: Option<ProcessExit>,
|
||||||
term_signal: Option<int>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
|
@ -82,7 +81,6 @@ impl Process {
|
||||||
home: get_handle_to_current_scheduler!(),
|
home: get_handle_to_current_scheduler!(),
|
||||||
to_wake: None,
|
to_wake: None,
|
||||||
exit_status: None,
|
exit_status: None,
|
||||||
term_signal: None,
|
|
||||||
};
|
};
|
||||||
match unsafe {
|
match unsafe {
|
||||||
uvll::uv_spawn(loop_.handle, handle, &options)
|
uvll::uv_spawn(loop_.handle, handle, &options)
|
||||||
|
@ -106,9 +104,10 @@ extern fn on_exit(handle: *uvll::uv_process_t,
|
||||||
let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
|
let p: &mut Process = unsafe { UvHandle::from_uv_handle(&handle) };
|
||||||
|
|
||||||
assert!(p.exit_status.is_none());
|
assert!(p.exit_status.is_none());
|
||||||
assert!(p.term_signal.is_none());
|
p.exit_status = Some(match term_signal {
|
||||||
p.exit_status = Some(exit_status as int);
|
0 => ExitStatus(exit_status as int),
|
||||||
p.term_signal = Some(term_signal as int);
|
n => ExitSignal(n as int),
|
||||||
|
});
|
||||||
|
|
||||||
match p.to_wake.take() {
|
match p.to_wake.take() {
|
||||||
Some(task) => {
|
Some(task) => {
|
||||||
|
@ -209,7 +208,7 @@ impl RtioProcess for Process {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait(&mut self) -> int {
|
fn wait(&mut self) -> ProcessExit {
|
||||||
// Make sure (on the home scheduler) that we have an exit status listed
|
// Make sure (on the home scheduler) that we have an exit status listed
|
||||||
let _m = self.fire_homing_missile();
|
let _m = self.fire_homing_missile();
|
||||||
match self.exit_status {
|
match self.exit_status {
|
||||||
|
@ -223,7 +222,6 @@ impl RtioProcess for Process {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(#10109): this is wrong
|
|
||||||
self.exit_status.unwrap()
|
self.exit_status.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ use rt::io;
|
||||||
use rt::io::io_error;
|
use rt::io::io_error;
|
||||||
use rt::rtio::{RtioProcess, IoFactory, with_local_io};
|
use rt::rtio::{RtioProcess, IoFactory, with_local_io};
|
||||||
|
|
||||||
|
use fmt;
|
||||||
|
|
||||||
// windows values don't matter as long as they're at least one of unix's
|
// windows values don't matter as long as they're at least one of unix's
|
||||||
// TERM/KILL/INT signals
|
// TERM/KILL/INT signals
|
||||||
#[cfg(windows)] pub static PleaseExitSignal: int = 15;
|
#[cfg(windows)] pub static PleaseExitSignal: int = 15;
|
||||||
|
@ -79,6 +81,40 @@ pub enum StdioContainer {
|
||||||
CreatePipe(bool /* readable */, bool /* writable */),
|
CreatePipe(bool /* readable */, bool /* writable */),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describes the result of a process after it has terminated.
|
||||||
|
#[deriving(Eq)]
|
||||||
|
pub enum ProcessExit {
|
||||||
|
/// Normal termination with an exit status.
|
||||||
|
ExitStatus(int),
|
||||||
|
|
||||||
|
/// Termination by signal, with the signal number.
|
||||||
|
ExitSignal(int),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Default for ProcessExit {
|
||||||
|
/// Format a ProcessExit enum, to nicely present the information.
|
||||||
|
fn fmt(obj: &ProcessExit, f: &mut fmt::Formatter) {
|
||||||
|
match *obj {
|
||||||
|
ExitStatus(code) => write!(f.buf, "exit code: {}", code),
|
||||||
|
ExitSignal(code) => write!(f.buf, "signal: {}", code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessExit {
|
||||||
|
/// Was termination successful? Signal termination not considered a success,
|
||||||
|
/// and success is defined as a zero exit status.
|
||||||
|
pub fn success(&self) -> bool {
|
||||||
|
return self.matches_exit_status(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks whether this ProcessExit matches the given exit status.
|
||||||
|
/// Termination by signal will never match an exit code.
|
||||||
|
pub fn matches_exit_status(&self, wanted: int) -> bool {
|
||||||
|
*self == ExitStatus(wanted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
/// Creates a new pipe initialized, but not bound to any particular
|
/// Creates a new pipe initialized, but not bound to any particular
|
||||||
/// source/destination
|
/// source/destination
|
||||||
|
@ -122,7 +158,7 @@ impl Process {
|
||||||
/// Wait for the child to exit completely, returning the status that it
|
/// Wait for the child to exit completely, returning the status that it
|
||||||
/// exited with. This function will continue to have the same return value
|
/// exited with. This function will continue to have the same return value
|
||||||
/// after it has been called at least once.
|
/// after it has been called at least once.
|
||||||
pub fn wait(&mut self) -> int { self.handle.wait() }
|
pub fn wait(&mut self) -> ProcessExit { self.handle.wait() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Process {
|
impl Drop for Process {
|
||||||
|
|
|
@ -18,7 +18,7 @@ use c_str::CString;
|
||||||
use ai = rt::io::net::addrinfo;
|
use ai = rt::io::net::addrinfo;
|
||||||
use rt::io::IoError;
|
use rt::io::IoError;
|
||||||
use rt::io::signal::Signum;
|
use rt::io::signal::Signum;
|
||||||
use super::io::process::ProcessConfig;
|
use super::io::process::{ProcessConfig, ProcessExit};
|
||||||
use super::io::net::ip::{IpAddr, SocketAddr};
|
use super::io::net::ip::{IpAddr, SocketAddr};
|
||||||
use path::Path;
|
use path::Path;
|
||||||
use super::io::{SeekStyle};
|
use super::io::{SeekStyle};
|
||||||
|
@ -201,7 +201,7 @@ pub trait RtioFileStream {
|
||||||
pub trait RtioProcess {
|
pub trait RtioProcess {
|
||||||
fn id(&self) -> libc::pid_t;
|
fn id(&self) -> libc::pid_t;
|
||||||
fn kill(&mut self, signal: int) -> Result<(), IoError>;
|
fn kill(&mut self, signal: int) -> Result<(), IoError>;
|
||||||
fn wait(&mut self) -> int;
|
fn wait(&mut self) -> ProcessExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RtioPipe {
|
pub trait RtioPipe {
|
||||||
|
|
|
@ -18,6 +18,7 @@ use libc::{pid_t, c_int};
|
||||||
use libc;
|
use libc;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use rt::io::process;
|
use rt::io::process;
|
||||||
|
use rt::io::process::ProcessExit;
|
||||||
use rt::io;
|
use rt::io;
|
||||||
use rt::io::Reader;
|
use rt::io::Reader;
|
||||||
use task;
|
use task;
|
||||||
|
@ -100,7 +101,7 @@ impl <'self> ProcessOptions<'self> {
|
||||||
/// The output of a finished process.
|
/// The output of a finished process.
|
||||||
pub struct ProcessOutput {
|
pub struct ProcessOutput {
|
||||||
/// The status (exit code) of the process.
|
/// The status (exit code) of the process.
|
||||||
status: int,
|
status: ProcessExit,
|
||||||
|
|
||||||
/// The data that the process wrote to stdout.
|
/// The data that the process wrote to stdout.
|
||||||
output: ~[u8],
|
output: ~[u8],
|
||||||
|
@ -194,7 +195,7 @@ impl Process {
|
||||||
*
|
*
|
||||||
* If the child has already been finished then the exit code is returned.
|
* If the child has already been finished then the exit code is returned.
|
||||||
*/
|
*/
|
||||||
pub fn finish(&mut self) -> int { self.inner.wait() }
|
pub fn finish(&mut self) -> ProcessExit { self.inner.wait() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the handle to stdin, waits for the child process to terminate, and
|
* Closes the handle to stdin, waits for the child process to terminate, and
|
||||||
|
@ -296,7 +297,7 @@ impl Process {
|
||||||
*
|
*
|
||||||
* The process's exit code
|
* The process's exit code
|
||||||
*/
|
*/
|
||||||
pub fn process_status(prog: &str, args: &[~str]) -> int {
|
pub fn process_status(prog: &str, args: &[~str]) -> ProcessExit {
|
||||||
let mut prog = Process::new(prog, args, ProcessOptions {
|
let mut prog = Process::new(prog, args, ProcessOptions {
|
||||||
env: None,
|
env: None,
|
||||||
dir: None,
|
dir: None,
|
||||||
|
@ -340,8 +341,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
||||||
fn test_process_status() {
|
fn test_process_status() {
|
||||||
assert_eq!(run::process_status("false", []), 1);
|
let mut status = run::process_status("false", []);
|
||||||
assert_eq!(run::process_status("true", []), 0);
|
assert!(status.matches_exit_status(1));
|
||||||
|
|
||||||
|
status = run::process_status("true", []);
|
||||||
|
assert!(status.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -352,7 +356,7 @@ mod tests {
|
||||||
= run::process_output("echo", [~"hello"]);
|
= run::process_output("echo", [~"hello"]);
|
||||||
let output_str = str::from_utf8(output);
|
let output_str = str::from_utf8(output);
|
||||||
|
|
||||||
assert_eq!(status, 0);
|
assert!(status.success());
|
||||||
assert_eq!(output_str.trim().to_owned(), ~"hello");
|
assert_eq!(output_str.trim().to_owned(), ~"hello");
|
||||||
// FIXME #7224
|
// FIXME #7224
|
||||||
if !running_on_valgrind() {
|
if !running_on_valgrind() {
|
||||||
|
@ -367,7 +371,7 @@ mod tests {
|
||||||
let run::ProcessOutput {status, output, error}
|
let run::ProcessOutput {status, output, error}
|
||||||
= run::process_output("mkdir", [~"."]);
|
= run::process_output("mkdir", [~"."]);
|
||||||
|
|
||||||
assert_eq!(status, 1);
|
assert!(status.matches_exit_status(1));
|
||||||
assert_eq!(output, ~[]);
|
assert_eq!(output, ~[]);
|
||||||
assert!(!error.is_empty());
|
assert!(!error.is_empty());
|
||||||
}
|
}
|
||||||
|
@ -424,15 +428,15 @@ mod tests {
|
||||||
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
||||||
fn test_finish_once() {
|
fn test_finish_once() {
|
||||||
let mut prog = run::Process::new("false", [], run::ProcessOptions::new());
|
let mut prog = run::Process::new("false", [], run::ProcessOptions::new());
|
||||||
assert_eq!(prog.finish(), 1);
|
assert!(prog.finish().matches_exit_status(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
||||||
fn test_finish_twice() {
|
fn test_finish_twice() {
|
||||||
let mut prog = run::Process::new("false", [], run::ProcessOptions::new());
|
let mut prog = run::Process::new("false", [], run::ProcessOptions::new());
|
||||||
assert_eq!(prog.finish(), 1);
|
assert!(prog.finish().matches_exit_status(1));
|
||||||
assert_eq!(prog.finish(), 1);
|
assert!(prog.finish().matches_exit_status(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -444,7 +448,7 @@ mod tests {
|
||||||
= prog.finish_with_output();
|
= prog.finish_with_output();
|
||||||
let output_str = str::from_utf8(output);
|
let output_str = str::from_utf8(output);
|
||||||
|
|
||||||
assert_eq!(status, 0);
|
assert!(status.success());
|
||||||
assert_eq!(output_str.trim().to_owned(), ~"hello");
|
assert_eq!(output_str.trim().to_owned(), ~"hello");
|
||||||
// FIXME #7224
|
// FIXME #7224
|
||||||
if !running_on_valgrind() {
|
if !running_on_valgrind() {
|
||||||
|
@ -462,7 +466,7 @@ mod tests {
|
||||||
|
|
||||||
let output_str = str::from_utf8(output);
|
let output_str = str::from_utf8(output);
|
||||||
|
|
||||||
assert_eq!(status, 0);
|
assert!(status.success());
|
||||||
assert_eq!(output_str.trim().to_owned(), ~"hello");
|
assert_eq!(output_str.trim().to_owned(), ~"hello");
|
||||||
// FIXME #7224
|
// FIXME #7224
|
||||||
if !running_on_valgrind() {
|
if !running_on_valgrind() {
|
||||||
|
@ -472,7 +476,7 @@ mod tests {
|
||||||
let run::ProcessOutput {status, output, error}
|
let run::ProcessOutput {status, output, error}
|
||||||
= prog.finish_with_output();
|
= prog.finish_with_output();
|
||||||
|
|
||||||
assert_eq!(status, 0);
|
assert!(status.success());
|
||||||
assert_eq!(output, ~[]);
|
assert_eq!(output, ~[]);
|
||||||
// FIXME #7224
|
// FIXME #7224
|
||||||
if !running_on_valgrind() {
|
if !running_on_valgrind() {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
// See #9341
|
// See #9341
|
||||||
|
|
||||||
use std::rt::io;
|
use std::rt::io;
|
||||||
|
use std::rt::io::process;
|
||||||
use std::rt::io::process::{Process, ProcessConfig, CreatePipe, Ignored};
|
use std::rt::io::process::{Process, ProcessConfig, CreatePipe, Ignored};
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ fn smoke() {
|
||||||
let p = Process::new(args);
|
let p = Process::new(args);
|
||||||
assert!(p.is_some());
|
assert!(p.is_some());
|
||||||
let mut p = p.unwrap();
|
let mut p = p.unwrap();
|
||||||
assert_eq!(p.wait(), 0);
|
assert!(p.wait().success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -78,7 +79,27 @@ fn exit_reported_right() {
|
||||||
let p = Process::new(args);
|
let p = Process::new(args);
|
||||||
assert!(p.is_some());
|
assert!(p.is_some());
|
||||||
let mut p = p.unwrap();
|
let mut p = p.unwrap();
|
||||||
assert_eq!(p.wait(), 1);
|
assert!(p.wait().matches_exit_status(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
fn signal_reported_right() {
|
||||||
|
let io = ~[];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"kill -1 $$"],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
let p = Process::new(args);
|
||||||
|
assert!(p.is_some());
|
||||||
|
let mut p = p.unwrap();
|
||||||
|
match p.wait() {
|
||||||
|
process::ExitSignal(1) => {},
|
||||||
|
result => fail!("not terminated by signal 1 (instead, {})", result),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_all(input: &mut Reader) -> ~str {
|
fn read_all(input: &mut Reader) -> ~str {
|
||||||
|
@ -100,7 +121,7 @@ fn run_output(args: ProcessConfig) -> ~str {
|
||||||
assert!(p.io[0].is_none());
|
assert!(p.io[0].is_none());
|
||||||
assert!(p.io[1].is_some());
|
assert!(p.io[1].is_some());
|
||||||
let ret = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
let ret = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
||||||
assert_eq!(p.wait(), 0);
|
assert!(p.wait().success());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +173,6 @@ fn stdin_works() {
|
||||||
p.io[0].get_mut_ref().write("foobar".as_bytes());
|
p.io[0].get_mut_ref().write("foobar".as_bytes());
|
||||||
p.io[0] = None; // close stdin;
|
p.io[0] = None; // close stdin;
|
||||||
let out = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
let out = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
||||||
assert_eq!(p.wait(), 0);
|
assert!(p.wait().success());
|
||||||
assert_eq!(out, ~"foobar\n");
|
assert_eq!(out, ~"foobar\n");
|
||||||
}
|
}
|
||||||
|
|
29
src/test/run-pass/signal-exit-status.rs
Normal file
29
src/test/run-pass/signal-exit-status.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// copyright 2013 the rust project developers. see the copyright
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/copyright.
|
||||||
|
//
|
||||||
|
// licensed under the apache license, version 2.0 <license-apache or
|
||||||
|
// http://www.apache.org/licenses/license-2.0> or the mit license
|
||||||
|
// <license-mit or http://opensource.org/licenses/mit>, at your
|
||||||
|
// option. this file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// xfail-fast
|
||||||
|
|
||||||
|
use std::{os, run};
|
||||||
|
use std::rt::io::process;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = os::args();
|
||||||
|
if args.len() >= 2 && args[1] == ~"signal" {
|
||||||
|
// Raise a segfault.
|
||||||
|
unsafe { *(0 as *mut int) = 0; }
|
||||||
|
} else {
|
||||||
|
let status = run::process_status(args[0], [~"signal"]);
|
||||||
|
match status {
|
||||||
|
process::ExitSignal(_) => {},
|
||||||
|
_ => fail!("invalid termination (was not signalled): {:?}", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue