Make process-spawning take environments and working directories, remove procsrv task from compiletest.
This commit is contained in:
parent
5131216fa6
commit
93450abb4b
18 changed files with 328 additions and 286 deletions
|
@ -28,5 +28,3 @@ type config =
|
||||||
runtool: option<str>,
|
runtool: option<str>,
|
||||||
rustcflags: option<str>,
|
rustcflags: option<str>,
|
||||||
verbose: bool};
|
verbose: bool};
|
||||||
|
|
||||||
type cx = {config: config, procsrv: procsrv::handle};
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import comm::chan;
|
||||||
import comm::send;
|
import comm::send;
|
||||||
import comm::recv;
|
import comm::recv;
|
||||||
|
|
||||||
import common::cx;
|
|
||||||
import common::config;
|
import common::config;
|
||||||
import common::mode_run_pass;
|
import common::mode_run_pass;
|
||||||
import common::mode_run_fail;
|
import common::mode_run_fail;
|
||||||
|
@ -113,10 +112,8 @@ fn mode_str(mode: mode) -> str {
|
||||||
|
|
||||||
fn run_tests(config: config) {
|
fn run_tests(config: config) {
|
||||||
let opts = test_opts(config);
|
let opts = test_opts(config);
|
||||||
let cx = {config: config, procsrv: procsrv::mk()};
|
let tests = make_tests(config);
|
||||||
let tests = make_tests(cx);
|
|
||||||
let res = test::run_tests_console(opts, tests);
|
let res = test::run_tests_console(opts, tests);
|
||||||
procsrv::close(cx.procsrv);
|
|
||||||
if !res { fail "Some tests failed"; }
|
if !res { fail "Some tests failed"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,14 +126,14 @@ fn test_opts(config: config) -> test::test_opts {
|
||||||
run_ignored: config.run_ignored}
|
run_ignored: config.run_ignored}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_tests(cx: cx) -> [test::test_desc] {
|
fn make_tests(config: config) -> [test::test_desc] {
|
||||||
#debug("making tests from %s", cx.config.src_base);
|
#debug("making tests from %s", config.src_base);
|
||||||
let tests = [];
|
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;
|
let file = file;
|
||||||
#debug("inspecting file %s", file);
|
#debug("inspecting file %s", file);
|
||||||
if is_test(cx.config, file) {
|
if is_test(config, file) {
|
||||||
tests += [make_test(cx, file)]
|
tests += [make_test(config, file)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret tests;
|
ret tests;
|
||||||
|
@ -162,12 +159,12 @@ fn is_test(config: config, testfile: str) -> bool {
|
||||||
ret valid;
|
ret valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_test(cx: cx, testfile: str) ->
|
fn make_test(config: config, testfile: str) ->
|
||||||
test::test_desc {
|
test::test_desc {
|
||||||
{
|
{
|
||||||
name: make_test_name(cx.config, testfile),
|
name: make_test_name(config, testfile),
|
||||||
fn: make_test_closure(cx, testfile),
|
fn: make_test_closure(config, testfile),
|
||||||
ignore: header::is_test_ignored(cx.config, testfile),
|
ignore: header::is_test_ignored(config, testfile),
|
||||||
should_fail: false
|
should_fail: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,24 +173,12 @@ fn make_test_name(config: config, testfile: str) -> str {
|
||||||
#fmt["[%s] %s", mode_str(config.mode), testfile]
|
#fmt["[%s] %s", mode_str(config.mode), testfile]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_test_closure(cx: cx, testfile: str) -> test::test_fn {
|
fn make_test_closure(config: config, testfile: str) -> test::test_fn {
|
||||||
let config = cx.config;
|
|
||||||
let chan = cx.procsrv.chan;
|
|
||||||
ret {||
|
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:
|
// Local Variables:
|
||||||
// fill-column: 78;
|
// fill-column: 78;
|
||||||
// indent-tabs-mode: nil
|
// 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;
|
||||||
|
import std::run::spawn_process;
|
||||||
import std::io;
|
import std::io;
|
||||||
|
import std::os;
|
||||||
import io::writer_util;
|
import io::writer_util;
|
||||||
import comm::chan;
|
|
||||||
import comm::port;
|
|
||||||
import comm::send;
|
|
||||||
import comm::recv;
|
|
||||||
import ctypes::{pid_t, fd_t};
|
import ctypes::{pid_t, fd_t};
|
||||||
|
|
||||||
export handle;
|
|
||||||
export mk;
|
|
||||||
export from_chan;
|
|
||||||
export run;
|
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 =
|
let env = std::generic_os::env();
|
||||||
{task: option<(task::task, port<task::task_notification>)>,
|
|
||||||
chan: reqchan};
|
|
||||||
|
|
||||||
enum request { exec([u8], [u8], [[u8]], chan<response>), stop, }
|
env = vec::map(env) {|pair|
|
||||||
|
let (k,v) = pair;
|
||||||
type response = {pid: pid_t, infd: fd_t,
|
if k == "PATH" { ("PATH", v + ";" + lib_path) }
|
||||||
outfd: fd_t, errfd: fd_t};
|
else { (k,v) }
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
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} }
|
#[cfg(target_os = "linux")]
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
fn close(handle: handle) {
|
#[cfg(target_os = "freebsd")]
|
||||||
send(handle.chan, stop);
|
fn target_env(lib_path: str, prog: str) -> option<[(str,str)]> {
|
||||||
task::join(option::get(handle.task));
|
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} {
|
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 pipe_in = os::pipe();
|
||||||
let output = readclose(resp.outfd);
|
let pipe_out = os::pipe();
|
||||||
let errput = readclose(resp.errfd);
|
let pipe_err = os::pipe();
|
||||||
let status = run::waitpid(resp.pid);
|
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};
|
ret {status: status, out: output, err: errput};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,106 +79,3 @@ fn readclose(fd: fd_t) -> str {
|
||||||
os::fclose(file);
|
os::fclose(file);
|
||||||
ret buf;
|
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_run_fail;
|
||||||
import common::mode_compile_fail;
|
import common::mode_compile_fail;
|
||||||
import common::mode_pretty;
|
import common::mode_pretty;
|
||||||
import common::cx;
|
|
||||||
import common::config;
|
import common::config;
|
||||||
import header::load_props;
|
import header::load_props;
|
||||||
import header::test_props;
|
import header::test_props;
|
||||||
|
@ -15,23 +14,23 @@ import util::logv;
|
||||||
|
|
||||||
export run;
|
export run;
|
||||||
|
|
||||||
fn run(cx: cx, testfile: str) {
|
fn run(config: config, testfile: str) {
|
||||||
if cx.config.verbose {
|
if config.verbose {
|
||||||
// We're going to be dumping a lot of info. Start on a new line.
|
// We're going to be dumping a lot of info. Start on a new line.
|
||||||
io::stdout().write_str("\n\n");
|
io::stdout().write_str("\n\n");
|
||||||
}
|
}
|
||||||
#debug("running %s", testfile);
|
#debug("running %s", testfile);
|
||||||
let props = load_props(testfile);
|
let props = load_props(testfile);
|
||||||
alt cx.config.mode {
|
alt config.mode {
|
||||||
mode_compile_fail { run_cfail_test(cx, props, testfile); }
|
mode_compile_fail { run_cfail_test(config, props, testfile); }
|
||||||
mode_run_fail { run_rfail_test(cx, props, testfile); }
|
mode_run_fail { run_rfail_test(config, props, testfile); }
|
||||||
mode_run_pass { run_rpass_test(cx, props, testfile); }
|
mode_run_pass { run_rpass_test(config, props, testfile); }
|
||||||
mode_pretty { run_pretty_test(cx, props, testfile); }
|
mode_pretty { run_pretty_test(config, props, testfile); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_cfail_test(cx: cx, props: test_props, testfile: str) {
|
fn run_cfail_test(config: config, props: test_props, testfile: str) {
|
||||||
let procres = compile_test(cx, props, testfile);
|
let procres = compile_test(config, props, testfile);
|
||||||
|
|
||||||
if procres.status == 0 {
|
if procres.status == 0 {
|
||||||
fatal_procres("compile-fail test compiled successfully!", procres);
|
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) {
|
fn run_rfail_test(config: config, props: test_props, testfile: str) {
|
||||||
let procres = compile_test(cx, props, testfile);
|
let procres = compile_test(config, props, testfile);
|
||||||
|
|
||||||
if procres.status != 0 { fatal_procres("compilation failed!", procres); }
|
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
|
// The value our Makefile configures valgrind to return on failure
|
||||||
const valgrind_err: int = 100;
|
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) {
|
fn run_rpass_test(config: config, props: test_props, testfile: str) {
|
||||||
let procres = compile_test(cx, props, testfile);
|
let procres = compile_test(config, props, testfile);
|
||||||
|
|
||||||
if procres.status != 0 { fatal_procres("compilation failed!", procres); }
|
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); }
|
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) {
|
if option::is_some(props.pp_exact) {
|
||||||
logv(cx.config, "testing for exact pretty-printing");
|
logv(config, "testing for exact pretty-printing");
|
||||||
} else { logv(cx.config, "testing for converging pretty-printing"); }
|
} else { logv(config, "testing for converging pretty-printing"); }
|
||||||
|
|
||||||
let rounds =
|
let rounds =
|
||||||
alt props.pp_exact { option::some(_) { 1 } option::none { 2 } };
|
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;
|
let round = 0;
|
||||||
while round < rounds {
|
while round < rounds {
|
||||||
logv(cx.config, #fmt["pretty-printing round %d", round]);
|
logv(config, #fmt["pretty-printing round %d", round]);
|
||||||
let procres = print_source(cx, testfile, srcs[round]);
|
let procres = print_source(config, testfile, srcs[round]);
|
||||||
|
|
||||||
if procres.status != 0 {
|
if procres.status != 0 {
|
||||||
fatal_procres(#fmt["pretty-printing failed in round %d", round],
|
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);
|
compare_source(expected, actual);
|
||||||
|
|
||||||
// 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(cx, testfile, actual);
|
let procres = typecheck_source(config, testfile, actual);
|
||||||
|
|
||||||
if procres.status != 0 {
|
if procres.status != 0 {
|
||||||
fatal_procres("pretty-printed source does not typecheck", procres);
|
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;
|
ret;
|
||||||
|
|
||||||
fn print_source(cx: cx, testfile: str, src: str) -> procres {
|
fn print_source(config: config, testfile: str, src: str) -> procres {
|
||||||
compose_and_run(cx, testfile, make_pp_args,
|
compose_and_run(config, testfile, make_pp_args,
|
||||||
cx.config.compile_lib_path, option::some(src))
|
config.compile_lib_path, option::some(src))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_pp_args(config: config, _testfile: str) -> procargs {
|
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 {
|
fn typecheck_source(config: config, testfile: str, src: str) -> procres {
|
||||||
compose_and_run(cx, testfile, make_typecheck_args,
|
compose_and_run(config, testfile, make_typecheck_args,
|
||||||
cx.config.compile_lib_path, option::some(src))
|
config.compile_lib_path, option::some(src))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_typecheck_args(config: config, _testfile: str) -> procargs {
|
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};
|
type procres = {status: int, stdout: str, stderr: str, cmdline: str};
|
||||||
|
|
||||||
fn compile_test(cx: cx, props: test_props, testfile: str) -> procres {
|
fn compile_test(config: config, props: test_props,
|
||||||
compose_and_run(cx, testfile, bind make_compile_args(_, props, _),
|
testfile: str) -> procres {
|
||||||
cx.config.compile_lib_path, option::none)
|
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 {
|
fn exec_compiled_test(config: config, props: test_props,
|
||||||
compose_and_run(cx, testfile, bind make_run_args(_, props, _),
|
testfile: str) -> procres {
|
||||||
cx.config.run_lib_path, option::none)
|
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,
|
make_args: fn@(config, str) -> procargs, lib_path: str,
|
||||||
input: option<str>) -> procres {
|
input: option<str>) -> procres {
|
||||||
let procargs = make_args(cx.config, testfile);
|
let procargs = make_args(config, testfile);
|
||||||
ret program_output(cx, testfile, lib_path, procargs.prog, procargs.args,
|
ret program_output(config, testfile, lib_path,
|
||||||
input);
|
procargs.prog, procargs.args, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_compile_args(config: config, props: test_props, testfile: str) ->
|
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 {
|
args: [str], input: option<str>) -> procres {
|
||||||
let cmdline =
|
let cmdline =
|
||||||
{
|
{
|
||||||
let cmdline = make_cmdline(lib_path, prog, args);
|
let cmdline = make_cmdline(lib_path, prog, args);
|
||||||
logv(cx.config, #fmt["executing %s", cmdline]);
|
logv(config, #fmt["executing %s", cmdline]);
|
||||||
cmdline
|
cmdline
|
||||||
};
|
};
|
||||||
let res = procsrv::run(cx.procsrv, lib_path, prog, args, input);
|
let res = procsrv::run(lib_path, prog, args, input);
|
||||||
dump_output(cx.config, testfile, res.out, res.err);
|
dump_output(config, testfile, res.out, res.err);
|
||||||
ret {status: res.status,
|
ret {status: res.status,
|
||||||
stdout: res.out,
|
stdout: res.out,
|
||||||
stderr: res.err,
|
stderr: res.err,
|
||||||
|
|
|
@ -1298,15 +1298,6 @@ const tag_five_b: uint = 248u;
|
||||||
const max_five_b: uint = 67108864u;
|
const max_five_b: uint = 67108864u;
|
||||||
const tag_six_b: uint = 252u;
|
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
|
Function: as_buf
|
||||||
|
|
||||||
|
@ -1319,7 +1310,10 @@ Example:
|
||||||
|
|
||||||
*/
|
*/
|
||||||
fn as_buf<T>(s: str, f: fn(sbuf) -> T) -> T unsafe {
|
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); }
|
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
|
Module: unsafe
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,6 +20,7 @@ export target_os;
|
||||||
export dylib_filename;
|
export dylib_filename;
|
||||||
export get_exe_path;
|
export get_exe_path;
|
||||||
export fsync_fd;
|
export fsync_fd;
|
||||||
|
export rustrt;
|
||||||
|
|
||||||
// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
|
// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
|
||||||
// by https://github.com/graydon/rust/issues#issue/268
|
// by https://github.com/graydon/rust/issues#issue/268
|
||||||
|
@ -116,6 +117,7 @@ fn waitpid(pid: pid_t) -> i32 {
|
||||||
|
|
||||||
#[abi = "cdecl"]
|
#[abi = "cdecl"]
|
||||||
native mod rustrt {
|
native mod rustrt {
|
||||||
|
fn rust_env_pairs() -> [str];
|
||||||
fn rust_getcwd() -> str;
|
fn rust_getcwd() -> str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,21 +11,23 @@ import core::option;
|
||||||
// Wow, this is an ugly way to write doc comments
|
// Wow, this is an ugly way to write doc comments
|
||||||
|
|
||||||
#[cfg(bogus)]
|
#[cfg(bogus)]
|
||||||
/*
|
#[doc = "Get the value of an environment variable"]
|
||||||
Function: getenv
|
|
||||||
|
|
||||||
Get the value of an environment variable
|
|
||||||
*/
|
|
||||||
fn getenv(n: str) -> option<str> { }
|
fn getenv(n: str) -> option<str> { }
|
||||||
|
|
||||||
#[cfg(bogus)]
|
#[cfg(bogus)]
|
||||||
/*
|
#[doc = "Set the value of an environment variable"]
|
||||||
Function: setenv
|
|
||||||
|
|
||||||
Set the value of an environment variable
|
|
||||||
*/
|
|
||||||
fn setenv(n: str, v: str) { }
|
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 = "linux")]
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[cfg(target_os = "freebsd")]
|
#[cfg(target_os = "freebsd")]
|
||||||
|
|
|
@ -20,6 +20,7 @@ export target_os;
|
||||||
export dylib_filename;
|
export dylib_filename;
|
||||||
export get_exe_path;
|
export get_exe_path;
|
||||||
export fsync_fd;
|
export fsync_fd;
|
||||||
|
export rustrt;
|
||||||
|
|
||||||
// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
|
// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult
|
||||||
// by https://github.com/graydon/rust/issues#issue/268
|
// by https://github.com/graydon/rust/issues#issue/268
|
||||||
|
@ -112,6 +113,7 @@ fn waitpid(pid: pid_t) -> i32 {
|
||||||
|
|
||||||
#[abi = "cdecl"]
|
#[abi = "cdecl"]
|
||||||
native mod rustrt {
|
native mod rustrt {
|
||||||
|
fn rust_env_pairs() -> [str];
|
||||||
fn rust_getcwd() -> str;
|
fn rust_getcwd() -> str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ export target_os;
|
||||||
export dylib_filename;
|
export dylib_filename;
|
||||||
export get_exe_path;
|
export get_exe_path;
|
||||||
export fsync_fd;
|
export fsync_fd;
|
||||||
|
export rustrt;
|
||||||
|
|
||||||
// FIXME Refactor into unix_os module or some such. Doesn't
|
// FIXME Refactor into unix_os module or some such. Doesn't
|
||||||
// seem to work right now.
|
// seem to work right now.
|
||||||
|
@ -115,6 +116,7 @@ fn fsync_fd(fd: fd_t, level: io::fsync::level) -> c_int {
|
||||||
|
|
||||||
#[abi = "cdecl"]
|
#[abi = "cdecl"]
|
||||||
native mod rustrt {
|
native mod rustrt {
|
||||||
|
fn rust_env_pairs() -> [str];
|
||||||
fn rust_getcwd() -> str;
|
fn rust_getcwd() -> str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ Process spawning
|
||||||
*/
|
*/
|
||||||
import option::{some, none};
|
import option::{some, none};
|
||||||
import str::sbuf;
|
import str::sbuf;
|
||||||
import ctypes::{fd_t, pid_t};
|
import ctypes::{fd_t, pid_t, void};
|
||||||
|
|
||||||
export program;
|
export program;
|
||||||
export run_program;
|
export run_program;
|
||||||
|
@ -16,8 +16,9 @@ export waitpid;
|
||||||
|
|
||||||
#[abi = "cdecl"]
|
#[abi = "cdecl"]
|
||||||
native mod rustrt {
|
native mod rustrt {
|
||||||
fn rust_run_program(argv: *sbuf, in_fd: fd_t,
|
fn rust_run_program(argv: *sbuf, envp: *void, dir: sbuf,
|
||||||
out_fd: fd_t, err_fd: fd_t) -> pid_t;
|
in_fd: fd_t, out_fd: fd_t, err_fd: fd_t)
|
||||||
|
-> pid_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Section: Types */
|
/* Section: Types */
|
||||||
|
@ -82,13 +83,6 @@ iface program {
|
||||||
|
|
||||||
/* Section: Operations */
|
/* 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
|
Function: spawn_process
|
||||||
|
|
||||||
|
@ -98,6 +92,8 @@ Parameters:
|
||||||
|
|
||||||
prog - The path to an executable
|
prog - The path to an executable
|
||||||
args - Vector of arguments to pass to the child process
|
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
|
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
|
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
|
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
|
The process id of the spawned process
|
||||||
*/
|
*/
|
||||||
fn spawn_process(prog: str, args: [str], in_fd: fd_t,
|
fn spawn_process(prog: str, args: [str],
|
||||||
out_fd: fd_t, err_fd: fd_t)
|
env: option<[(str,str)]>,
|
||||||
|
dir: option<str>,
|
||||||
|
in_fd: fd_t, out_fd: fd_t, err_fd: fd_t)
|
||||||
-> pid_t unsafe {
|
-> pid_t unsafe {
|
||||||
// Note: we have to hold on to these vector references while we hold a
|
with_argv(prog, args) {|argv|
|
||||||
// pointer to their buffers
|
with_envp(env) { |envp|
|
||||||
let prog = prog;
|
with_dirp(dir) { |dirp|
|
||||||
let args = vec::map(args, {|arg| @arg });
|
rustrt::rust_run_program(argv, envp, dirp,
|
||||||
let argv = arg_vec(prog, args);
|
in_fd, out_fd, err_fd)
|
||||||
let pid =
|
}
|
||||||
rustrt::rust_run_program(vec::unsafe::to_ptr(argv), in_fd, out_fd,
|
}
|
||||||
err_fd);
|
}
|
||||||
ret pid;
|
}
|
||||||
|
|
||||||
|
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
|
The process id
|
||||||
*/
|
*/
|
||||||
fn run_program(prog: str, args: [str]) -> int {
|
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_output = os::pipe();
|
||||||
let pipe_err = os::pipe();
|
let pipe_err = os::pipe();
|
||||||
let pid =
|
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);
|
pipe_err.out);
|
||||||
|
|
||||||
if pid == -1i32 { fail; }
|
if pid == -1i32 { fail; }
|
||||||
|
@ -316,7 +386,8 @@ mod tests {
|
||||||
|
|
||||||
let pid =
|
let pid =
|
||||||
run::spawn_process(
|
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_in.in);
|
||||||
os::close(pipe_out.out);
|
os::close(pipe_out.out);
|
||||||
os::close(pipe_err.out);
|
os::close(pipe_err.out);
|
||||||
|
@ -356,7 +427,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn waitpid() {
|
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);
|
let status = run::waitpid(pid);
|
||||||
assert status == 1;
|
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 {
|
fn get_concurrency() -> uint {
|
||||||
let threads = rustrt::sched_threads();
|
let threads = rustrt::sched_threads();
|
||||||
if threads == 1u { 1u }
|
if threads == 1u { 1u }
|
||||||
else { threads * 4u }
|
else { threads * sched_overcommit }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_tests(opts: test_opts,
|
fn filter_tests(opts: test_opts,
|
||||||
|
|
|
@ -106,6 +106,7 @@ fn fsync_fd(_fd: fd_t, _level: io::fsync::level) -> c_int {
|
||||||
|
|
||||||
#[abi = "cdecl"]
|
#[abi = "cdecl"]
|
||||||
native mod rustrt {
|
native mod rustrt {
|
||||||
|
fn rust_env_pairs() -> [str];
|
||||||
fn rust_process_wait(handle: c_int) -> c_int;
|
fn rust_process_wait(handle: c_int) -> c_int;
|
||||||
fn rust_getcwd() -> str;
|
fn rust_getcwd() -> str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,16 +41,7 @@ command_line_args : public kernel_owned<command_line_args>
|
||||||
LocalFree(wargv);
|
LocalFree(wargv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
args = (rust_vec *)
|
args = make_str_vec(kernel, argc, argv);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~command_line_args() {
|
~command_line_args() {
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
#include "rust_scheduler.h"
|
#include "rust_scheduler.h"
|
||||||
#include "sync/timer.h"
|
#include "sync/timer.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <crt_externs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(__WIN32__)
|
#if !defined(__WIN32__)
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -73,6 +77,49 @@ rust_getcwd() {
|
||||||
return make_str(task->kernel, cbuf, strlen(cbuf), "rust_str(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.
|
// TODO: Allow calling native functions that return double results.
|
||||||
extern "C" CDECL
|
extern "C" CDECL
|
||||||
void squareroot(double *input, double *output) {
|
void squareroot(double *input, double *output) {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#include "rust_internal.h"
|
#include "rust_internal.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <crt_externs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__WIN32__)
|
#if defined(__WIN32__)
|
||||||
|
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
|
@ -64,7 +68,10 @@ void append_arg(char *& buf, char const *arg, bool last) {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" CDECL int
|
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;
|
STARTUPINFO si;
|
||||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||||
si.cb = 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;
|
PROCESS_INFORMATION pi;
|
||||||
BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
|
BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
|
||||||
0, NULL, NULL, &si, &pi);
|
0, envp, dir, &si, &pi);
|
||||||
|
|
||||||
CloseHandle(si.hStdInput);
|
CloseHandle(si.hStdInput);
|
||||||
CloseHandle(si.hStdOutput);
|
CloseHandle(si.hStdOutput);
|
||||||
|
@ -130,7 +137,10 @@ rust_process_wait(int proc) {
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
extern "C" CDECL int
|
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();
|
int pid = fork();
|
||||||
if (pid != 0) return pid;
|
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);
|
if (err_fd) dup2(err_fd, 2);
|
||||||
/* Close all other fds. */
|
/* Close all other fds. */
|
||||||
for (int fd = getdtablesize() - 1; fd >= 3; fd--) close(fd);
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,6 +204,21 @@ make_str(rust_kernel* kernel, const char* c, size_t strlen, const char* name) {
|
||||||
return str;
|
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:
|
// Local Variables:
|
||||||
// mode: C++
|
// mode: C++
|
||||||
|
|
|
@ -42,6 +42,7 @@ rust_run_program
|
||||||
rust_set_exit_status
|
rust_set_exit_status
|
||||||
rust_start
|
rust_start
|
||||||
rust_getcwd
|
rust_getcwd
|
||||||
|
rust_env_pairs
|
||||||
rust_task_yield
|
rust_task_yield
|
||||||
rust_task_is_unwinding
|
rust_task_is_unwinding
|
||||||
rust_get_task
|
rust_get_task
|
||||||
|
|
Loading…
Reference in a new issue