From 93450abb4bf6a755b343ca459bbeff92540a7822 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Tue, 7 Feb 2012 18:55:02 -0800 Subject: [PATCH] Make process-spawning take environments and working directories, remove procsrv task from compiletest. --- src/compiletest/common.rs | 2 - src/compiletest/compiletest.rs | 39 ++----- src/compiletest/procsrv.rs | 196 +++++++-------------------------- src/compiletest/runtest.rs | 85 +++++++------- src/libcore/str.rs | 14 +-- src/libcore/vec.rs | 12 ++ src/libstd/freebsd_os.rs | 2 + src/libstd/generic_os.rs | 22 ++-- src/libstd/linux_os.rs | 2 + src/libstd/macos_os.rs | 2 + src/libstd/run_program.rs | 123 ++++++++++++++++----- src/libstd/test.rs | 11 +- src/libstd/win32_os.rs | 1 + src/rt/rust.cpp | 11 +- src/rt/rust_builtin.cpp | 47 ++++++++ src/rt/rust_run_program.cpp | 29 ++++- src/rt/rust_util.h | 15 +++ src/rt/rustrt.def.in | 1 + 18 files changed, 328 insertions(+), 286 deletions(-) diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index bb5e629ef62..7f2c46bef48 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -28,5 +28,3 @@ type config = runtool: option, rustcflags: option, verbose: bool}; - -type cx = {config: config, procsrv: procsrv::handle}; diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 32f8762577f..4fc510e9c62 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -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 diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs index 4d01d299548..892ed15fb33 100644 --- a/src/compiletest/procsrv.rs +++ b/src/compiletest/procsrv.rs @@ -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; +#[cfg(target_os = "win32")] +fn target_env(lib_path: str, prog: str) -> option<[(str,str)]> { -type handle = - {task: option<(task::task, port)>, - chan: reqchan}; + let env = std::generic_os::env(); -enum request { exec([u8], [u8], [[u8]], chan), 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) -> {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) { - - // 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(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(_path: str, f: fn@() -> T) -> T { - f() -} - -fn with_lib_path(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; -} diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 375625fc794..68b821bc1c9 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -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) -> 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] { } } -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) -> 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, diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 0c03aca1245..7af87f77ecf 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -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(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 } /* diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 0dc5dea59bc..28c93718e5b 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1077,6 +1077,18 @@ FIXME: We don't need this wrapper */ unsafe fn to_ptr(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(v: [const E], f: fn(*E) -> T) -> T unsafe { + let buf = unsafe::to_ptr(v); f(buf) +} + + /* Module: unsafe */ diff --git a/src/libstd/freebsd_os.rs b/src/libstd/freebsd_os.rs index 315813c0848..d7ca27b550b 100644 --- a/src/libstd/freebsd_os.rs +++ b/src/libstd/freebsd_os.rs @@ -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; } diff --git a/src/libstd/generic_os.rs b/src/libstd/generic_os.rs index f41e83fa5fb..0f9f8a58410 100644 --- a/src/libstd/generic_os.rs +++ b/src/libstd/generic_os.rs @@ -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 { } #[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")] diff --git a/src/libstd/linux_os.rs b/src/libstd/linux_os.rs index 05722333df8..0231add3cd4 100644 --- a/src/libstd/linux_os.rs +++ b/src/libstd/linux_os.rs @@ -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; } diff --git a/src/libstd/macos_os.rs b/src/libstd/macos_os.rs index 920061cfd31..e44bc4c20e2 100644 --- a/src/libstd/macos_os.rs +++ b/src/libstd/macos_os.rs @@ -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; } diff --git a/src/libstd/run_program.rs b/src/libstd/run_program.rs index f81eda3aeeb..fa827c96f2f 100644 --- a/src/libstd/run_program.rs +++ b/src/libstd/run_program.rs @@ -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, + 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(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(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(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(d: option, + 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; } diff --git a/src/libstd/test.rs b/src/libstd/test.rs index 6d7273ff0b1..8d5ecda2c48 100644 --- a/src/libstd/test.rs +++ b/src/libstd/test.rs @@ -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, diff --git a/src/libstd/win32_os.rs b/src/libstd/win32_os.rs index ddc1ebd7a35..aa589cf3c32 100644 --- a/src/libstd/win32_os.rs +++ b/src/libstd/win32_os.rs @@ -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; } diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp index b5af3942a20..dacbcee1045 100644 --- a/src/rt/rust.cpp +++ b/src/rt/rust.cpp @@ -41,16 +41,7 @@ command_line_args : public kernel_owned LocalFree(wargv); #endif - args = (rust_vec *) - kernel->malloc(vec_size(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() { diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 8fbe0ed7888..76f80192e89 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -7,6 +7,10 @@ #include "rust_scheduler.h" #include "sync/timer.h" +#ifdef __APPLE__ +#include +#endif + #if !defined(__WIN32__) #include #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(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) { diff --git a/src/rt/rust_run_program.cpp b/src/rt/rust_run_program.cpp index df4714758dd..848b015dd6c 100644 --- a/src/rt/rust_run_program.cpp +++ b/src/rt/rust_run_program.cpp @@ -1,5 +1,9 @@ #include "rust_internal.h" +#ifdef __APPLE__ +#include +#endif + #if defined(__WIN32__) #include @@ -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 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); } diff --git a/src/rt/rust_util.h b/src/rt/rust_util.h index 07569db3ffa..1b611dd0be6 100644 --- a/src/rt/rust_util.h +++ b/src/rt/rust_util.h @@ -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(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++ diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 48736ef029b..81f8a9c25f7 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -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