Make process-spawning take environments and working directories, remove procsrv task from compiletest.
This commit is contained in:
parent
5131216fa6
commit
93450abb4b
@ -28,5 +28,3 @@ type config =
|
||||
runtool: option<str>,
|
||||
rustcflags: option<str>,
|
||||
verbose: bool};
|
||||
|
||||
type cx = {config: config, procsrv: procsrv::handle};
|
||||
|
@ -17,7 +17,6 @@ import comm::chan;
|
||||
import comm::send;
|
||||
import comm::recv;
|
||||
|
||||
import common::cx;
|
||||
import common::config;
|
||||
import common::mode_run_pass;
|
||||
import common::mode_run_fail;
|
||||
@ -113,10 +112,8 @@ fn mode_str(mode: mode) -> str {
|
||||
|
||||
fn run_tests(config: config) {
|
||||
let opts = test_opts(config);
|
||||
let cx = {config: config, procsrv: procsrv::mk()};
|
||||
let tests = make_tests(cx);
|
||||
let tests = make_tests(config);
|
||||
let res = test::run_tests_console(opts, tests);
|
||||
procsrv::close(cx.procsrv);
|
||||
if !res { fail "Some tests failed"; }
|
||||
}
|
||||
|
||||
@ -129,14 +126,14 @@ fn test_opts(config: config) -> test::test_opts {
|
||||
run_ignored: config.run_ignored}
|
||||
}
|
||||
|
||||
fn make_tests(cx: cx) -> [test::test_desc] {
|
||||
#debug("making tests from %s", cx.config.src_base);
|
||||
fn make_tests(config: config) -> [test::test_desc] {
|
||||
#debug("making tests from %s", config.src_base);
|
||||
let tests = [];
|
||||
for file: str in fs::list_dir(cx.config.src_base) {
|
||||
for file: str in fs::list_dir(config.src_base) {
|
||||
let file = file;
|
||||
#debug("inspecting file %s", file);
|
||||
if is_test(cx.config, file) {
|
||||
tests += [make_test(cx, file)]
|
||||
if is_test(config, file) {
|
||||
tests += [make_test(config, file)]
|
||||
}
|
||||
}
|
||||
ret tests;
|
||||
@ -162,12 +159,12 @@ fn is_test(config: config, testfile: str) -> bool {
|
||||
ret valid;
|
||||
}
|
||||
|
||||
fn make_test(cx: cx, testfile: str) ->
|
||||
fn make_test(config: config, testfile: str) ->
|
||||
test::test_desc {
|
||||
{
|
||||
name: make_test_name(cx.config, testfile),
|
||||
fn: make_test_closure(cx, testfile),
|
||||
ignore: header::is_test_ignored(cx.config, testfile),
|
||||
name: make_test_name(config, testfile),
|
||||
fn: make_test_closure(config, testfile),
|
||||
ignore: header::is_test_ignored(config, testfile),
|
||||
should_fail: false
|
||||
}
|
||||
}
|
||||
@ -176,24 +173,12 @@ fn make_test_name(config: config, testfile: str) -> str {
|
||||
#fmt["[%s] %s", mode_str(config.mode), testfile]
|
||||
}
|
||||
|
||||
fn make_test_closure(cx: cx, testfile: str) -> test::test_fn {
|
||||
let config = cx.config;
|
||||
let chan = cx.procsrv.chan;
|
||||
fn make_test_closure(config: config, testfile: str) -> test::test_fn {
|
||||
ret {||
|
||||
run_test_task(config, chan, testfile);
|
||||
runtest::run(config, copy testfile);
|
||||
};
|
||||
}
|
||||
|
||||
fn run_test_task(config: common::config,
|
||||
procsrv_chan: procsrv::reqchan,
|
||||
testfile: str) {
|
||||
|
||||
let procsrv = procsrv::from_chan(procsrv_chan);
|
||||
let cx = {config: config, procsrv: procsrv};
|
||||
|
||||
runtest::run(cx, copy testfile);
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
|
@ -1,71 +1,60 @@
|
||||
// So when running tests in parallel there's a potential race on environment
|
||||
// variables if we let each task spawn its own children - between the time the
|
||||
// environment is set and the process is spawned another task could spawn its
|
||||
// child process. Because of that we have to use a complicated scheme with a
|
||||
// dedicated server for spawning processes.
|
||||
|
||||
import std::generic_os::setenv;
|
||||
import std::generic_os::getenv;
|
||||
import std::os;
|
||||
import std::run;
|
||||
import std::run::spawn_process;
|
||||
import std::io;
|
||||
import std::os;
|
||||
import io::writer_util;
|
||||
import comm::chan;
|
||||
import comm::port;
|
||||
import comm::send;
|
||||
import comm::recv;
|
||||
import ctypes::{pid_t, fd_t};
|
||||
|
||||
export handle;
|
||||
export mk;
|
||||
export from_chan;
|
||||
export run;
|
||||
export close;
|
||||
export reqchan;
|
||||
|
||||
type reqchan = chan<request>;
|
||||
#[cfg(target_os = "win32")]
|
||||
fn target_env(lib_path: str, prog: str) -> option<[(str,str)]> {
|
||||
|
||||
type handle =
|
||||
{task: option<(task::task, port<task::task_notification>)>,
|
||||
chan: reqchan};
|
||||
let env = std::generic_os::env();
|
||||
|
||||
enum request { exec([u8], [u8], [[u8]], chan<response>), stop, }
|
||||
|
||||
type response = {pid: pid_t, infd: fd_t,
|
||||
outfd: fd_t, errfd: fd_t};
|
||||
|
||||
fn mk() -> handle {
|
||||
let setupport = port();
|
||||
let setupchan = chan(setupport);
|
||||
let task = task::spawn_joinable {||
|
||||
let reqport = port();
|
||||
let reqchan = chan(reqport);
|
||||
send(setupchan, reqchan);
|
||||
worker(reqport);
|
||||
env = vec::map(env) {|pair|
|
||||
let (k,v) = pair;
|
||||
if k == "PATH" { ("PATH", v + ";" + lib_path) }
|
||||
else { (k,v) }
|
||||
};
|
||||
ret {task: option::some(task), chan: recv(setupport)};
|
||||
if str::ends_with(prog, "rustc.exe") {
|
||||
env += [("RUST_THREADS", "1")]
|
||||
}
|
||||
ret some(env);
|
||||
}
|
||||
|
||||
fn from_chan(ch: reqchan) -> handle { {task: option::none, chan: ch} }
|
||||
|
||||
fn close(handle: handle) {
|
||||
send(handle.chan, stop);
|
||||
task::join(option::get(handle.task));
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn target_env(lib_path: str, prog: str) -> option<[(str,str)]> {
|
||||
none
|
||||
}
|
||||
|
||||
fn run(handle: handle, lib_path: str, prog: str, args: [str],
|
||||
|
||||
fn run(lib_path: str, prog: str, args: [str],
|
||||
input: option<str>) -> {status: int, out: str, err: str} {
|
||||
let p = port();
|
||||
let ch = chan(p);
|
||||
send(handle.chan,
|
||||
exec(str::bytes(lib_path), str::bytes(prog), clone_vecstr(args),
|
||||
ch));
|
||||
let resp = recv(p);
|
||||
|
||||
writeclose(resp.infd, input);
|
||||
let output = readclose(resp.outfd);
|
||||
let errput = readclose(resp.errfd);
|
||||
let status = run::waitpid(resp.pid);
|
||||
let pipe_in = os::pipe();
|
||||
let pipe_out = os::pipe();
|
||||
let pipe_err = os::pipe();
|
||||
let pid = spawn_process(prog, args, target_env(lib_path, prog), none,
|
||||
pipe_in.in, pipe_out.out, pipe_err.out);
|
||||
|
||||
os::close(pipe_in.in);
|
||||
os::close(pipe_out.out);
|
||||
os::close(pipe_err.out);
|
||||
if pid == -1i32 {
|
||||
os::close(pipe_in.out);
|
||||
os::close(pipe_out.in);
|
||||
os::close(pipe_err.in);
|
||||
fail;
|
||||
}
|
||||
|
||||
|
||||
writeclose(pipe_in.out, input);
|
||||
let output = readclose(pipe_out.in);
|
||||
let errput = readclose(pipe_err.in);
|
||||
let status = run::waitpid(pid);
|
||||
ret {status: status, out: output, err: errput};
|
||||
}
|
||||
|
||||
@ -90,106 +79,3 @@ fn readclose(fd: fd_t) -> str {
|
||||
os::fclose(file);
|
||||
ret buf;
|
||||
}
|
||||
|
||||
fn worker(p: port<request>) {
|
||||
|
||||
// FIXME (787): If we declare this inside of the while loop and then
|
||||
// break out of it before it's ever initialized (i.e. we don't run
|
||||
// any tests), then the cleanups will puke.
|
||||
let execparms;
|
||||
|
||||
while true {
|
||||
// FIXME: Sending strings across channels seems to still
|
||||
// leave them refed on the sender's end, which causes problems if
|
||||
// the receiver's poniters outlive the sender's. Here we clone
|
||||
// everything and let the originals go out of scope before sending
|
||||
// a response.
|
||||
execparms =
|
||||
{
|
||||
|
||||
// FIXME (785): The 'discriminant' of an alt expression has
|
||||
// the same scope as the alt expression itself, so we have to
|
||||
// put the entire alt in another block to make sure the exec
|
||||
// message goes out of scope. Seems like the scoping rules for
|
||||
// the alt discriminant are wrong.
|
||||
alt recv(p) {
|
||||
exec(lib_path, prog, args, respchan) {
|
||||
{lib_path: str::from_bytes(lib_path),
|
||||
prog: str::from_bytes(prog),
|
||||
args: clone_vecu8str(args),
|
||||
respchan: respchan}
|
||||
}
|
||||
stop { ret }
|
||||
}
|
||||
};
|
||||
|
||||
// This is copied from run::start_program
|
||||
let pipe_in = os::pipe();
|
||||
let pipe_out = os::pipe();
|
||||
let pipe_err = os::pipe();
|
||||
let spawnproc =
|
||||
bind run::spawn_process(execparms.prog, execparms.args,
|
||||
pipe_in.in, pipe_out.out, pipe_err.out);
|
||||
let pid = maybe_with_lib_path(execparms.lib_path, spawnproc);
|
||||
|
||||
os::close(pipe_in.in);
|
||||
os::close(pipe_out.out);
|
||||
os::close(pipe_err.out);
|
||||
if pid == -1i32 {
|
||||
os::close(pipe_in.out);
|
||||
os::close(pipe_out.in);
|
||||
os::close(pipe_err.in);
|
||||
fail;
|
||||
}
|
||||
|
||||
send(execparms.respchan,
|
||||
{pid: pid,
|
||||
infd: pipe_in.out,
|
||||
outfd: pipe_out.in,
|
||||
errfd: pipe_err.in});
|
||||
}
|
||||
}
|
||||
|
||||
// Only windows needs to set the library path
|
||||
#[cfg(target_os = "win32")]
|
||||
fn maybe_with_lib_path<T>(path: str, f: fn@() -> T) -> T {
|
||||
with_lib_path(path, f)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn maybe_with_lib_path<T>(_path: str, f: fn@() -> T) -> T {
|
||||
f()
|
||||
}
|
||||
|
||||
fn with_lib_path<T>(path: str, f: fn@() -> T) -> T {
|
||||
let maybe_oldpath = getenv(util::lib_path_env_var());
|
||||
append_lib_path(path);
|
||||
let res = f();
|
||||
if option::is_some(maybe_oldpath) {
|
||||
export_lib_path(option::get(maybe_oldpath));
|
||||
} else {
|
||||
// FIXME: This should really be unset but we don't have that yet
|
||||
export_lib_path("");
|
||||
}
|
||||
ret res;
|
||||
}
|
||||
|
||||
fn append_lib_path(path: str) { export_lib_path(util::make_new_path(path)); }
|
||||
|
||||
fn export_lib_path(path: str) { setenv(util::lib_path_env_var(), path); }
|
||||
|
||||
fn clone_vecstr(v: [str]) -> [[u8]] {
|
||||
let r = [];
|
||||
for t: str in vec::slice(v, 0u, vec::len(v)) { r += [str::bytes(t)]; }
|
||||
ret r;
|
||||
}
|
||||
|
||||
fn clone_vecu8str(v: [[u8]]) -> [str] {
|
||||
let r = [];
|
||||
for t in vec::slice(v, 0u, vec::len(v)) {
|
||||
r += [str::from_bytes(t)];
|
||||
}
|
||||
ret r;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import common::mode_run_pass;
|
||||
import common::mode_run_fail;
|
||||
import common::mode_compile_fail;
|
||||
import common::mode_pretty;
|
||||
import common::cx;
|
||||
import common::config;
|
||||
import header::load_props;
|
||||
import header::test_props;
|
||||
@ -15,23 +14,23 @@ import util::logv;
|
||||
|
||||
export run;
|
||||
|
||||
fn run(cx: cx, testfile: str) {
|
||||
if cx.config.verbose {
|
||||
fn run(config: config, testfile: str) {
|
||||
if config.verbose {
|
||||
// We're going to be dumping a lot of info. Start on a new line.
|
||||
io::stdout().write_str("\n\n");
|
||||
}
|
||||
#debug("running %s", testfile);
|
||||
let props = load_props(testfile);
|
||||
alt cx.config.mode {
|
||||
mode_compile_fail { run_cfail_test(cx, props, testfile); }
|
||||
mode_run_fail { run_rfail_test(cx, props, testfile); }
|
||||
mode_run_pass { run_rpass_test(cx, props, testfile); }
|
||||
mode_pretty { run_pretty_test(cx, props, testfile); }
|
||||
alt config.mode {
|
||||
mode_compile_fail { run_cfail_test(config, props, testfile); }
|
||||
mode_run_fail { run_rfail_test(config, props, testfile); }
|
||||
mode_run_pass { run_rpass_test(config, props, testfile); }
|
||||
mode_pretty { run_pretty_test(config, props, testfile); }
|
||||
}
|
||||
}
|
||||
|
||||
fn run_cfail_test(cx: cx, props: test_props, testfile: str) {
|
||||
let procres = compile_test(cx, props, testfile);
|
||||
fn run_cfail_test(config: config, props: test_props, testfile: str) {
|
||||
let procres = compile_test(config, props, testfile);
|
||||
|
||||
if procres.status == 0 {
|
||||
fatal_procres("compile-fail test compiled successfully!", procres);
|
||||
@ -50,12 +49,12 @@ fn run_cfail_test(cx: cx, props: test_props, testfile: str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_rfail_test(cx: cx, props: test_props, testfile: str) {
|
||||
let procres = compile_test(cx, props, testfile);
|
||||
fn run_rfail_test(config: config, props: test_props, testfile: str) {
|
||||
let procres = compile_test(config, props, testfile);
|
||||
|
||||
if procres.status != 0 { fatal_procres("compilation failed!", procres); }
|
||||
|
||||
procres = exec_compiled_test(cx, props, testfile);
|
||||
procres = exec_compiled_test(config, props, testfile);
|
||||
|
||||
// The value our Makefile configures valgrind to return on failure
|
||||
const valgrind_err: int = 100;
|
||||
@ -78,21 +77,21 @@ fn check_correct_failure_status(procres: procres) {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_rpass_test(cx: cx, props: test_props, testfile: str) {
|
||||
let procres = compile_test(cx, props, testfile);
|
||||
fn run_rpass_test(config: config, props: test_props, testfile: str) {
|
||||
let procres = compile_test(config, props, testfile);
|
||||
|
||||
if procres.status != 0 { fatal_procres("compilation failed!", procres); }
|
||||
|
||||
procres = exec_compiled_test(cx, props, testfile);
|
||||
procres = exec_compiled_test(config, props, testfile);
|
||||
|
||||
|
||||
if procres.status != 0 { fatal_procres("test run failed!", procres); }
|
||||
}
|
||||
|
||||
fn run_pretty_test(cx: cx, props: test_props, testfile: str) {
|
||||
fn run_pretty_test(config: config, props: test_props, testfile: str) {
|
||||
if option::is_some(props.pp_exact) {
|
||||
logv(cx.config, "testing for exact pretty-printing");
|
||||
} else { logv(cx.config, "testing for converging pretty-printing"); }
|
||||
logv(config, "testing for exact pretty-printing");
|
||||
} else { logv(config, "testing for converging pretty-printing"); }
|
||||
|
||||
let rounds =
|
||||
alt props.pp_exact { option::some(_) { 1 } option::none { 2 } };
|
||||
@ -101,8 +100,8 @@ fn run_pretty_test(cx: cx, props: test_props, testfile: str) {
|
||||
|
||||
let round = 0;
|
||||
while round < rounds {
|
||||
logv(cx.config, #fmt["pretty-printing round %d", round]);
|
||||
let procres = print_source(cx, testfile, srcs[round]);
|
||||
logv(config, #fmt["pretty-printing round %d", round]);
|
||||
let procres = print_source(config, testfile, srcs[round]);
|
||||
|
||||
if procres.status != 0 {
|
||||
fatal_procres(#fmt["pretty-printing failed in round %d", round],
|
||||
@ -134,7 +133,7 @@ fn run_pretty_test(cx: cx, props: test_props, testfile: str) {
|
||||
compare_source(expected, actual);
|
||||
|
||||
// Finally, let's make sure it actually appears to remain valid code
|
||||
let procres = typecheck_source(cx, testfile, actual);
|
||||
let procres = typecheck_source(config, testfile, actual);
|
||||
|
||||
if procres.status != 0 {
|
||||
fatal_procres("pretty-printed source does not typecheck", procres);
|
||||
@ -142,9 +141,9 @@ fn run_pretty_test(cx: cx, props: test_props, testfile: str) {
|
||||
|
||||
ret;
|
||||
|
||||
fn print_source(cx: cx, testfile: str, src: str) -> procres {
|
||||
compose_and_run(cx, testfile, make_pp_args,
|
||||
cx.config.compile_lib_path, option::some(src))
|
||||
fn print_source(config: config, testfile: str, src: str) -> procres {
|
||||
compose_and_run(config, testfile, make_pp_args,
|
||||
config.compile_lib_path, option::some(src))
|
||||
}
|
||||
|
||||
fn make_pp_args(config: config, _testfile: str) -> procargs {
|
||||
@ -173,9 +172,9 @@ actual:\n\
|
||||
}
|
||||
}
|
||||
|
||||
fn typecheck_source(cx: cx, testfile: str, src: str) -> procres {
|
||||
compose_and_run(cx, testfile, make_typecheck_args,
|
||||
cx.config.compile_lib_path, option::some(src))
|
||||
fn typecheck_source(config: config, testfile: str, src: str) -> procres {
|
||||
compose_and_run(config, testfile, make_typecheck_args,
|
||||
config.compile_lib_path, option::some(src))
|
||||
}
|
||||
|
||||
fn make_typecheck_args(config: config, _testfile: str) -> procargs {
|
||||
@ -286,22 +285,24 @@ type procargs = {prog: str, args: [str]};
|
||||
|
||||
type procres = {status: int, stdout: str, stderr: str, cmdline: str};
|
||||
|
||||
fn compile_test(cx: cx, props: test_props, testfile: str) -> procres {
|
||||
compose_and_run(cx, testfile, bind make_compile_args(_, props, _),
|
||||
cx.config.compile_lib_path, option::none)
|
||||
fn compile_test(config: config, props: test_props,
|
||||
testfile: str) -> procres {
|
||||
compose_and_run(config, testfile, bind make_compile_args(_, props, _),
|
||||
config.compile_lib_path, option::none)
|
||||
}
|
||||
|
||||
fn exec_compiled_test(cx: cx, props: test_props, testfile: str) -> procres {
|
||||
compose_and_run(cx, testfile, bind make_run_args(_, props, _),
|
||||
cx.config.run_lib_path, option::none)
|
||||
fn exec_compiled_test(config: config, props: test_props,
|
||||
testfile: str) -> procres {
|
||||
compose_and_run(config, testfile, bind make_run_args(_, props, _),
|
||||
config.run_lib_path, option::none)
|
||||
}
|
||||
|
||||
fn compose_and_run(cx: cx, testfile: str,
|
||||
fn compose_and_run(config: config, testfile: str,
|
||||
make_args: fn@(config, str) -> procargs, lib_path: str,
|
||||
input: option<str>) -> procres {
|
||||
let procargs = make_args(cx.config, testfile);
|
||||
ret program_output(cx, testfile, lib_path, procargs.prog, procargs.args,
|
||||
input);
|
||||
let procargs = make_args(config, testfile);
|
||||
ret program_output(config, testfile, lib_path,
|
||||
procargs.prog, procargs.args, input);
|
||||
}
|
||||
|
||||
fn make_compile_args(config: config, props: test_props, testfile: str) ->
|
||||
@ -354,16 +355,16 @@ fn split_maybe_args(argstr: option<str>) -> [str] {
|
||||
}
|
||||
}
|
||||
|
||||
fn program_output(cx: cx, testfile: str, lib_path: str, prog: str,
|
||||
fn program_output(config: config, testfile: str, lib_path: str, prog: str,
|
||||
args: [str], input: option<str>) -> procres {
|
||||
let cmdline =
|
||||
{
|
||||
let cmdline = make_cmdline(lib_path, prog, args);
|
||||
logv(cx.config, #fmt["executing %s", cmdline]);
|
||||
logv(config, #fmt["executing %s", cmdline]);
|
||||
cmdline
|
||||
};
|
||||
let res = procsrv::run(cx.procsrv, lib_path, prog, args, input);
|
||||
dump_output(cx.config, testfile, res.out, res.err);
|
||||
let res = procsrv::run(lib_path, prog, args, input);
|
||||
dump_output(config, testfile, res.out, res.err);
|
||||
ret {status: res.status,
|
||||
stdout: res.out,
|
||||
stderr: res.err,
|
||||
|
@ -1298,15 +1298,6 @@ const tag_five_b: uint = 248u;
|
||||
const max_five_b: uint = 67108864u;
|
||||
const tag_six_b: uint = 252u;
|
||||
|
||||
// NB: This is intentionally unexported because it's easy to misuse (there's
|
||||
// no guarantee that the string is rooted). Instead, use as_buf below.
|
||||
unsafe fn buf(s: str) -> sbuf {
|
||||
let saddr = ptr::addr_of(s);
|
||||
let vaddr: *[u8] = ::unsafe::reinterpret_cast(saddr);
|
||||
let buf = vec::to_ptr(*vaddr);
|
||||
ret buf;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: as_buf
|
||||
|
||||
@ -1319,7 +1310,10 @@ Example:
|
||||
|
||||
*/
|
||||
fn as_buf<T>(s: str, f: fn(sbuf) -> T) -> T unsafe {
|
||||
let buf = buf(s); f(buf)
|
||||
let v: [u8] = ::unsafe::reinterpret_cast(s);
|
||||
let r = vec::as_buf(v, f);
|
||||
::unsafe::leak(v);
|
||||
r
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1077,6 +1077,18 @@ FIXME: We don't need this wrapper
|
||||
*/
|
||||
unsafe fn to_ptr<T>(v: [T]) -> *T { ret unsafe::to_ptr(v); }
|
||||
|
||||
/*
|
||||
Function: as_buf
|
||||
|
||||
Work with the buffer of a vector. Allows for unsafe manipulation
|
||||
of vector contents, which is useful for native interop.
|
||||
|
||||
*/
|
||||
fn as_buf<E,T>(v: [const E], f: fn(*E) -> T) -> T unsafe {
|
||||
let buf = unsafe::to_ptr(v); f(buf)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Module: unsafe
|
||||
*/
|
||||
|
@ -20,6 +20,7 @@ export target_os;
|
||||
export dylib_filename;
|
||||
export get_exe_path;
|
||||
export fsync_fd;
|
||||
export rustrt;
|
||||
|
||||
// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
|
||||
// by https://github.com/graydon/rust/issues#issue/268
|
||||
@ -116,6 +117,7 @@ fn waitpid(pid: pid_t) -> i32 {
|
||||
|
||||
#[abi = "cdecl"]
|
||||
native mod rustrt {
|
||||
fn rust_env_pairs() -> [str];
|
||||
fn rust_getcwd() -> str;
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,23 @@ import core::option;
|
||||
// Wow, this is an ugly way to write doc comments
|
||||
|
||||
#[cfg(bogus)]
|
||||
/*
|
||||
Function: getenv
|
||||
|
||||
Get the value of an environment variable
|
||||
*/
|
||||
#[doc = "Get the value of an environment variable"]
|
||||
fn getenv(n: str) -> option<str> { }
|
||||
|
||||
#[cfg(bogus)]
|
||||
/*
|
||||
Function: setenv
|
||||
|
||||
Set the value of an environment variable
|
||||
*/
|
||||
#[doc = "Set the value of an environment variable"]
|
||||
fn setenv(n: str, v: str) { }
|
||||
|
||||
fn env() -> [(str,str)] {
|
||||
let pairs = [];
|
||||
for p in os::rustrt::rust_env_pairs() {
|
||||
let vs = str::split(p, '=' as u8);
|
||||
assert vec::len(vs) == 2u;
|
||||
pairs += [(vs[0], vs[1])];
|
||||
}
|
||||
ret pairs;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
|
@ -20,6 +20,7 @@ export target_os;
|
||||
export dylib_filename;
|
||||
export get_exe_path;
|
||||
export fsync_fd;
|
||||
export rustrt;
|
||||
|
||||
// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
|
||||
// by https://github.com/graydon/rust/issues#issue/268
|
||||
@ -112,6 +113,7 @@ fn waitpid(pid: pid_t) -> i32 {
|
||||
|
||||
#[abi = "cdecl"]
|
||||
native mod rustrt {
|
||||
fn rust_env_pairs() -> [str];
|
||||
fn rust_getcwd() -> str;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ export target_os;
|
||||
export dylib_filename;
|
||||
export get_exe_path;
|
||||
export fsync_fd;
|
||||
export rustrt;
|
||||
|
||||
// FIXME Refactor into unix_os module or some such. Doesn't
|
||||
// seem to work right now.
|
||||
@ -115,6 +116,7 @@ fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int {
|
||||
|
||||
#[abi = "cdecl"]
|
||||
native mod rustrt {
|
||||
fn rust_env_pairs() -> [str];
|
||||
fn rust_getcwd() -> str;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ Process spawning
|
||||
*/
|
||||
import option::{some, none};
|
||||
import str::sbuf;
|
||||
import ctypes::{fd_t, pid_t};
|
||||
import ctypes::{fd_t, pid_t, void};
|
||||
|
||||
export program;
|
||||
export run_program;
|
||||
@ -16,8 +16,9 @@ export waitpid;
|
||||
|
||||
#[abi = "cdecl"]
|
||||
native mod rustrt {
|
||||
fn rust_run_program(argv: *sbuf, in_fd: fd_t,
|
||||
out_fd: fd_t, err_fd: fd_t) -> pid_t;
|
||||
fn rust_run_program(argv: *sbuf, envp: *void, dir: sbuf,
|
||||
in_fd: fd_t, out_fd: fd_t, err_fd: fd_t)
|
||||
-> pid_t;
|
||||
}
|
||||
|
||||
/* Section: Types */
|
||||
@ -82,13 +83,6 @@ iface program {
|
||||
|
||||
/* Section: Operations */
|
||||
|
||||
fn arg_vec(prog: str, args: [@str]) -> [sbuf] {
|
||||
let argptrs = str::as_buf(prog, {|buf| [buf] });
|
||||
for arg in args { argptrs += str::as_buf(*arg, {|buf| [buf] }); }
|
||||
argptrs += [ptr::null()];
|
||||
ret argptrs;
|
||||
}
|
||||
|
||||
/*
|
||||
Function: spawn_process
|
||||
|
||||
@ -98,6 +92,8 @@ Parameters:
|
||||
|
||||
prog - The path to an executable
|
||||
args - Vector of arguments to pass to the child process
|
||||
env - optional env-modification for child
|
||||
dir - optional dir to run child in (default current dir)
|
||||
in_fd - A file descriptor for the child to use as std input
|
||||
out_fd - A file descriptor for the child to use as std output
|
||||
err_fd - A file descriptor for the child to use as std error
|
||||
@ -106,18 +102,90 @@ Returns:
|
||||
|
||||
The process id of the spawned process
|
||||
*/
|
||||
fn spawn_process(prog: str, args: [str], in_fd: fd_t,
|
||||
out_fd: fd_t, err_fd: fd_t)
|
||||
fn spawn_process(prog: str, args: [str],
|
||||
env: option<[(str,str)]>,
|
||||
dir: option<str>,
|
||||
in_fd: fd_t, out_fd: fd_t, err_fd: fd_t)
|
||||
-> pid_t unsafe {
|
||||
// Note: we have to hold on to these vector references while we hold a
|
||||
// pointer to their buffers
|
||||
let prog = prog;
|
||||
let args = vec::map(args, {|arg| @arg });
|
||||
let argv = arg_vec(prog, args);
|
||||
let pid =
|
||||
rustrt::rust_run_program(vec::unsafe::to_ptr(argv), in_fd, out_fd,
|
||||
err_fd);
|
||||
ret pid;
|
||||
with_argv(prog, args) {|argv|
|
||||
with_envp(env) { |envp|
|
||||
with_dirp(dir) { |dirp|
|
||||
rustrt::rust_run_program(argv, envp, dirp,
|
||||
in_fd, out_fd, err_fd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_argv<T>(prog: str, args: [str],
|
||||
cb: fn(*sbuf) -> T) -> T unsafe {
|
||||
let argptrs = str::as_buf(prog) {|b| [b] };
|
||||
let tmps = [];
|
||||
for arg in args {
|
||||
let t = @arg;
|
||||
tmps += [t];
|
||||
argptrs += str::as_buf(*t) {|b| [b] };
|
||||
}
|
||||
argptrs += [ptr::null()];
|
||||
vec::as_buf(argptrs, cb)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn with_envp<T>(env: option<[(str,str)]>,
|
||||
cb: fn(*void) -> T) -> T unsafe {
|
||||
// On posixy systems we can pass a char** for envp, which is
|
||||
// a null-terminated array of "k=v\n" strings.
|
||||
alt env {
|
||||
some (es) {
|
||||
let tmps = [];
|
||||
let ptrs = [];
|
||||
|
||||
for (k,v) in es {
|
||||
let t = @(#fmt("%s=%s", k, v));
|
||||
vec::push(tmps, t);
|
||||
ptrs += str::as_buf(*t) {|b| [b]};
|
||||
}
|
||||
ptrs += [ptr::null()];
|
||||
vec::as_buf(ptrs) { |p| cb(::unsafe::reinterpret_cast(p)) }
|
||||
}
|
||||
none {
|
||||
cb(ptr::null())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "win32")]
|
||||
fn with_envp<T>(env: option<[(str,str)]>,
|
||||
cb: fn(*void) -> T) -> T unsafe {
|
||||
// On win32 we pass an "environment block" which is not a char**, but
|
||||
// rather a concatenation of null-terminated k=v\0 sequences, with a final
|
||||
// \0 to terminate.
|
||||
alt env {
|
||||
some (es) {
|
||||
let blk : [u8] = [];
|
||||
for (k,v) in es {
|
||||
let t = #fmt("%s=%s", k, v);
|
||||
let v : [u8] = ::unsafe::reinterpret_cast(t);
|
||||
blk += v;
|
||||
::unsafe::leak(v);
|
||||
}
|
||||
blk += [0_u8];
|
||||
vec::as_buf(blk) {|p| cb(::unsafe::reinterpret_cast(p)) }
|
||||
}
|
||||
none {
|
||||
cb(ptr::null())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_dirp<T>(d: option<str>,
|
||||
cb: fn(sbuf) -> T) -> T unsafe {
|
||||
alt d {
|
||||
some(dir) { str::as_buf(dir, cb) }
|
||||
none { cb(ptr::null()) }
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -135,7 +203,8 @@ Returns:
|
||||
The process id
|
||||
*/
|
||||
fn run_program(prog: str, args: [str]) -> int {
|
||||
ret waitpid(spawn_process(prog, args, 0i32, 0i32, 0i32));
|
||||
ret waitpid(spawn_process(prog, args, none, none,
|
||||
0i32, 0i32, 0i32));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -161,7 +230,8 @@ fn start_program(prog: str, args: [str]) -> program {
|
||||
let pipe_output = os::pipe();
|
||||
let pipe_err = os::pipe();
|
||||
let pid =
|
||||
spawn_process(prog, args, pipe_input.in, pipe_output.out,
|
||||
spawn_process(prog, args, none, none,
|
||||
pipe_input.in, pipe_output.out,
|
||||
pipe_err.out);
|
||||
|
||||
if pid == -1i32 { fail; }
|
||||
@ -316,7 +386,8 @@ mod tests {
|
||||
|
||||
let pid =
|
||||
run::spawn_process(
|
||||
"cat", [], pipe_in.in, pipe_out.out, pipe_err.out);
|
||||
"cat", [], none, none,
|
||||
pipe_in.in, pipe_out.out, pipe_err.out);
|
||||
os::close(pipe_in.in);
|
||||
os::close(pipe_out.out);
|
||||
os::close(pipe_err.out);
|
||||
@ -356,7 +427,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn waitpid() {
|
||||
let pid = run::spawn_process("false", [], 0i32, 0i32, 0i32);
|
||||
let pid = run::spawn_process("false", [],
|
||||
none, none,
|
||||
0i32, 0i32, 0i32);
|
||||
let status = run::waitpid(pid);
|
||||
assert status == 1;
|
||||
}
|
||||
|
@ -227,10 +227,19 @@ fn run_tests(opts: test_opts, tests: [test_desc],
|
||||
}
|
||||
}
|
||||
|
||||
// Windows tends to dislike being overloaded with threads.
|
||||
#[cfg(target_os = "win32")]
|
||||
const sched_overcommit : uint = 1u;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg(target_os = "macos")]
|
||||
const sched_overcommit : uint = 4u;
|
||||
|
||||
fn get_concurrency() -> uint {
|
||||
let threads = rustrt::sched_threads();
|
||||
if threads == 1u { 1u }
|
||||
else { threads * 4u }
|
||||
else { threads * sched_overcommit }
|
||||
}
|
||||
|
||||
fn filter_tests(opts: test_opts,
|
||||
|
@ -106,6 +106,7 @@ fn fsync_fd(_fd: fd_t, _level: io::fsync::level) -> c_int {
|
||||
|
||||
#[abi = "cdecl"]
|
||||
native mod rustrt {
|
||||
fn rust_env_pairs() -> [str];
|
||||
fn rust_process_wait(handle: c_int) -> c_int;
|
||||
fn rust_getcwd() -> str;
|
||||
}
|
||||
|
@ -41,16 +41,7 @@ command_line_args : public kernel_owned<command_line_args>
|
||||
LocalFree(wargv);
|
||||
#endif
|
||||
|
||||
args = (rust_vec *)
|
||||
kernel->malloc(vec_size<rust_vec*>(argc),
|
||||
"command line arg interior");
|
||||
args->fill = args->alloc = sizeof(rust_vec*) * argc;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
rust_str *str = make_str(kernel, argv[i],
|
||||
strlen(argv[i]),
|
||||
"command line arg");
|
||||
((rust_str**)&args->data)[i] = str;
|
||||
}
|
||||
args = make_str_vec(kernel, argc, argv);
|
||||
}
|
||||
|
||||
~command_line_args() {
|
||||
|
@ -7,6 +7,10 @@
|
||||
#include "rust_scheduler.h"
|
||||
#include "sync/timer.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <crt_externs.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__WIN32__)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
@ -73,6 +77,49 @@ rust_getcwd() {
|
||||
return make_str(task->kernel, cbuf, strlen(cbuf), "rust_str(getcwd");
|
||||
}
|
||||
|
||||
|
||||
#if defined(__WIN32__)
|
||||
extern "C" CDECL rust_vec *
|
||||
rust_env_pairs() {
|
||||
rust_task *task = rust_task_thread::get_task();
|
||||
size_t envc = 0;
|
||||
LPTCH ch = GetEnvironmentStringsA();
|
||||
LPTCH c;
|
||||
for (c = ch; *c; c += strlen(c) + 1) {
|
||||
++envc;
|
||||
}
|
||||
c = ch;
|
||||
rust_vec *v = (rust_vec *)
|
||||
task->kernel->malloc(vec_size<rust_vec*>(envc),
|
||||
"str vec interior");
|
||||
v->fill = v->alloc = sizeof(rust_vec*) * envc;
|
||||
for (size_t i = 0; i < envc; ++i) {
|
||||
size_t n = strlen(c);
|
||||
rust_str *str = make_str(task->kernel, c, n, "str");
|
||||
((rust_str**)&v->data)[i] = str;
|
||||
c += n + 1;
|
||||
}
|
||||
if (ch) {
|
||||
FreeEnvironmentStrings(ch);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
#else
|
||||
extern "C" CDECL rust_vec *
|
||||
rust_env_pairs() {
|
||||
rust_task *task = rust_task_thread::get_task();
|
||||
#ifdef __APPLE__
|
||||
char **environ = *_NSGetEnviron();
|
||||
#endif
|
||||
char **e = environ;
|
||||
size_t envc = 0;
|
||||
while (*e) {
|
||||
++envc; ++e;
|
||||
}
|
||||
return make_str_vec(task->kernel, envc, environ);
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: Allow calling native functions that return double results.
|
||||
extern "C" CDECL
|
||||
void squareroot(double *input, double *output) {
|
||||
|
@ -1,5 +1,9 @@
|
||||
#include "rust_internal.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <crt_externs.h>
|
||||
#endif
|
||||
|
||||
#if defined(__WIN32__)
|
||||
|
||||
#include <process.h>
|
||||
@ -64,7 +68,10 @@ void append_arg(char *& buf, char const *arg, bool last) {
|
||||
}
|
||||
|
||||
extern "C" CDECL int
|
||||
rust_run_program(const char* argv[], int in_fd, int out_fd, int err_fd) {
|
||||
rust_run_program(const char* argv[],
|
||||
void* envp,
|
||||
const char* dir,
|
||||
int in_fd, int out_fd, int err_fd) {
|
||||
STARTUPINFO si;
|
||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
@ -99,7 +106,7 @@ rust_run_program(const char* argv[], int in_fd, int out_fd, int err_fd) {
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
|
||||
0, NULL, NULL, &si, &pi);
|
||||
0, envp, dir, &si, &pi);
|
||||
|
||||
CloseHandle(si.hStdInput);
|
||||
CloseHandle(si.hStdOutput);
|
||||
@ -130,7 +137,10 @@ rust_process_wait(int proc) {
|
||||
#include <termios.h>
|
||||
|
||||
extern "C" CDECL int
|
||||
rust_run_program(char* argv[], int in_fd, int out_fd, int err_fd) {
|
||||
rust_run_program(const char* argv[],
|
||||
void* envp,
|
||||
const char* dir,
|
||||
int in_fd, int out_fd, int err_fd) {
|
||||
int pid = fork();
|
||||
if (pid != 0) return pid;
|
||||
|
||||
@ -143,7 +153,18 @@ rust_run_program(char* argv[], int in_fd, int out_fd, int err_fd) {
|
||||
if (err_fd) dup2(err_fd, 2);
|
||||
/* Close all other fds. */
|
||||
for (int fd = getdtablesize() - 1; fd >= 3; fd--) close(fd);
|
||||
execvp(argv[0], argv);
|
||||
if (dir) { chdir(dir); }
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (envp) {
|
||||
*_NSGetEnviron() = (char **)envp;
|
||||
}
|
||||
execvp(argv[0], (char * const *)argv);
|
||||
#else
|
||||
if (!envp) { envp = environ; }
|
||||
execvpe(argv[0], (char * const *)argv, (char * const *)envp);
|
||||
#endif
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -204,6 +204,21 @@ make_str(rust_kernel* kernel, const char* c, size_t strlen, const char* name) {
|
||||
return str;
|
||||
}
|
||||
|
||||
inline rust_vec *
|
||||
make_str_vec(rust_kernel* kernel, size_t nstrs, char **strs) {
|
||||
rust_vec *v = (rust_vec *)
|
||||
kernel->malloc(vec_size<rust_vec*>(nstrs),
|
||||
"str vec interior");
|
||||
v->fill = v->alloc = sizeof(rust_vec*) * nstrs;
|
||||
for (size_t i = 0; i < nstrs; ++i) {
|
||||
rust_str *str = make_str(kernel, strs[i],
|
||||
strlen(strs[i]),
|
||||
"str");
|
||||
((rust_str**)&v->data)[i] = str;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: C++
|
||||
|
@ -42,6 +42,7 @@ rust_run_program
|
||||
rust_set_exit_status
|
||||
rust_start
|
||||
rust_getcwd
|
||||
rust_env_pairs
|
||||
rust_task_yield
|
||||
rust_task_is_unwinding
|
||||
rust_get_task
|
||||
|
Loading…
x
Reference in New Issue
Block a user