diff --git a/src/testrunner.pr b/src/testrunner.pr index 9637396..473be15 100644 --- a/src/testrunner.pr +++ b/src/testrunner.pr @@ -118,6 +118,9 @@ if runner { #if defined WIN32 { const dll_ext = ".dll" const dev_null = "nul" +} else if defined MACOS { + const dll_ext = ".dylib" + const dev_null = "/dev/null" } else { const dll_ext = ".so" const dev_null = "/dev/null" @@ -278,7 +281,7 @@ def enumerate_dir(file_or_dir: String, pattern: String, recursive: bool, res: &V res.push(file_or_dir) return } else if sres.st_mode & 0o170000 == 0o040000 { - var dir: *linux::s___dirstream + var dir: Dir if not (dir = linux::opendir(file_or_dir.to_array().value)) { return } diff --git a/std/io.pr b/std/io.pr index a4334f9..1ead585 100644 --- a/std/io.pr +++ b/std/io.pr @@ -92,7 +92,12 @@ export const NO_BLOCKING = 1 export const _IONBF = 2 const F_SETFL = 4 - const O_NONBLOCK = 2048 + + #if defined MACOS { + const O_NONBLOCK = 4 + } else { + const O_NONBLOCK = 2048 + } export def pipe(mode: int = 0) -> File, File { var res: [2; int] diff --git a/std/process.pr b/std/process.pr index 71284f0..bace69f 100644 --- a/std/process.pr +++ b/std/process.pr @@ -129,8 +129,14 @@ import std linux::execvp(exe.to_array().value, pargs.value) exit(1) } - - let fd = linux::syscall(SYS_pidfd_open, pid, 2048 /* PIDFD_NONBLOCK */) + + #if defined MACOS { + // MacOS has no facility to open a process as a file discriptor, + // so we have to use something other than poll + let fd = 0 + } else { + let fd = linux::syscall(SYS_pidfd_open, pid, 2048 /* PIDFD_NONBLOCK */) + } return [ pid = pid, fd = fd, @@ -151,29 +157,77 @@ import std return (status & 0xFF00) >> 8 } - export def wait(process: *Process, timeout: ulong = -1) { - var pfd = [fd = process.fd !int, events = POLLIN, revents = 0] !linux::s_pollfd; - if linux::poll(*pfd, 1, timeout !int) == 0 { - return + #if defined MACOS { + var gtimeout = false + def on_timeout(sig: int) { + gtimeout = true } + } - var stat_val: int - if linux::waitpid(process.pid, *stat_val, 0) == -1 { - abort("Waitpid error\n") - } - if WIFSIGNALED(stat_val) { - process.exit_code = 128 + WTERMSIG(stat_val) - } else if WIFEXITED(stat_val) { - process.exit_code = WEXITSTATUS(stat_val) + export def wait(process: *Process, timeout: ulong = -1) { + #if defined MACOS { + // We need to do something stupid with signals here to mimick a timeout + gtimeout = false + let old_handler = cstd::signal(cstd::SIGALRM, *on_timeout) + if timeout < -1 { + linux::ualarm((timeout * 1000) !int, 0) + } + + while not gtimeout { + var stat_val: int + + let status = linux::waitpid(process.pid, *stat_val, 1 /* WNOHANG */) + if status == -1 { + abort("Waitpid error\n") + } else if status == 0 { + linux::pause() + continue + } + + if WIFSIGNALED(stat_val) { + process.exit_code = 128 + WTERMSIG(stat_val) + } else if WIFEXITED(stat_val) { + process.exit_code = WEXITSTATUS(stat_val) + } else { + abort("Invalid signal\n") + } + + process.running = false + + linux::alarm(0) + cstd::signal(cstd::SIGALRM, old_handler) + return + } + + cstd::signal(cstd::SIGALRM, old_handler) } else { - abort("Invalid signal\n") - } + var pfd = [fd = process.fd !int, events = POLLIN, revents = 0] !linux::s_pollfd; + if linux::poll(*pfd, 1, timeout !int) == 0 { + return + } - process.running = false + var stat_val: int + if linux::waitpid(process.pid, *stat_val, 1 /* WNOHANG */) == -1 { + abort("Waitpid error\n") + } + if WIFSIGNALED(stat_val) { + process.exit_code = 128 + WTERMSIG(stat_val) + } else if WIFEXITED(stat_val) { + process.exit_code = WEXITSTATUS(stat_val) + } else { + abort("Invalid signal\n") + } + + process.running = false + } } export def terminate(process: *Process) { - linux::kill(process.pid, SIGTERM) + #if defined MACOS { + cstd::kill(process.pid, SIGTERM) + } else { + linux::kill(process.pid, SIGTERM) + } } export def dispose(process: *Process) { diff --git a/std/shared.pr b/std/shared.pr index 1c5c793..240913f 100644 --- a/std/shared.pr +++ b/std/shared.pr @@ -127,6 +127,85 @@ export def find_symbol(library: *Library, name: String) -> Optional(Symbol) { } windows::FreeLibrary(library.handle !*windows::s_HINSTANCE__) } +} else if defined MACOS { + import linux + import io + import process + + const RTLD_NOW = 2 + + export def load(path: String, init: bool = true) -> Library { + + let handle = linux::dlopen(path.to_array().value, RTLD_NOW) + if not handle { abort("Couldn't open shared library " + path + "\n") } + + // We're going to call nm here because it seems to be the most straight forward way of enumerating symbols + // If you know a better way of doing this please tell me. + + let r, w = io::pipe(io::NO_BLOCKING) + let args = allocate_ref(String, 2) + args(0) = "-gU" + args(1) = path + + let proc = *process::spawn("/usr/bin/nm", args, stdout = w) + var data: Str + while proc.running { + proc.wait(100) + data = data + r.read_remaining() + } + proc.dispose() + let nm_symbols = data.split('\n') + + let symbols = vector::make(Symbol) + + for var sym in @nm_symbols { + let name = sym.substring(20) + let nm_kind = sym(17) + + if nm_kind == 'D' or nm_kind == 'S' or nm_kind == 'T' { + var err = dlerror() + symbols.push([ + kind = SymbolKind::FUNCTION if nm_kind == 'T' else SymbolKind::OBJECT, + name = name, + value = dlsym(handle, name.to_array().value) + ] !Symbol) + err = dlerror() + if err { + print("Couldn't load symbol ", name, ": ", err, "\n") + } + } + } + + let library = [ + path = path, + handle = handle, + symbols = symbols.to_array() + ] !Library + + if init { + let main_sym = find_symbol(*library, "main::__main__::([[char]])") + if main_sym.exists { + let main = main_sym.get().value !(def ([[char]]) -> []) + // FIXME Pass in actual command line arguments + let args = zero_allocate(type [char], 0) + main(args) + delete(args) + } + } + + return library + } + + export def close(library: *Library, finalize: bool = true) { + if finalize { + let finalizer_sym = find_symbol(library, "main::__finalizer__::()") + if finalizer_sym.exists { + let finalizer = finalizer_sym.get().value !(def -> []) + finalizer() + } + } + linux::dlclose(library.handle) + } } else { import linux @@ -164,7 +243,7 @@ export def find_symbol(library: *Library, name: String) -> Optional(Symbol) { export def load(path: String, init: bool = true) -> Library { let fh = open(path, "rb") if not fh { - abort("Couldn't open library " + path + " (" + make_string(cstd::strerror(@linux::__errno_location())) + ")\n") + abort("Couldn't open library " + path + " (" + std::c_error_str() + ")\n") } defer close(fh) diff --git a/std/std.pr b/std/std.pr index 3691f29..a4d004a 100644 --- a/std/std.pr +++ b/std/std.pr @@ -191,6 +191,7 @@ from strings export * import linux import macos export type File = *cstd::s___sFILE + export type Dir = *linux::s_DIR export def stdin -> File { return cstd::__stdinp } export def stdout -> File { return cstd::__stdoutp } @@ -199,7 +200,8 @@ from strings export * export const PATH_MAX = 4096 } else { import linux - export type File = *cstd::s__IO_FILE + export type File = *cstd::s__IO_FIL + export type Dir = *linux::s___dirstream export def stdin -> File { return cstd::stdin } export def stdout -> File { return cstd::stdout } @@ -227,6 +229,16 @@ export const SEEK_SET: int = 0 export const SEEK_CUR: int = 1 export const SEEK_END: int = 2 +export def c_error_str -> Str { + #if defined MACOS { + return make_string(cstd::strerror(@linux::__error())) + } else if defined LINUX { + return make_string(cstd::strerror(@liunx::__errno_location())) + } else { + // TODO Implement for Windows + } +} + // TODO This is causing an ambigious reference error, it really shouldn't /*export def combine_hashes(a: uint64, b: uint64) -> uint64 { var hash: uint64 = 17 diff --git a/std/strings.pr b/std/strings.pr index 4e3c4d7..4f1952f 100644 --- a/std/strings.pr +++ b/std/strings.pr @@ -2,6 +2,7 @@ import cstd import std import runtime import optional +import vector export def length(s: [char]) -> size_t { return s.size - 1 @@ -739,6 +740,24 @@ export def match(pattern: String, candidate: String) -> bool { return match(pattern, candidate, 0, 0) } +export def split(input: String, separator: char) -> &[Str] { + let res = vector::make(Str) + + var last = 0 + for var i in 0..input.length { + if input(i) == separator { + res.push(input.substring(last, i)) + i += 1 + last = i + } + } + if last < input.length { + res.push(input.substring(last, input.length)) + } + + return res.to_array() +} + export def utf8_encode(code_point: uint64) -> Str { var res: StringBuffer = "" if code_point <= 0x007F {