diff --git a/self_hosted/paths.jou b/self_hosted/paths.jou index 457f61eb..7c2da3a5 100644 --- a/self_hosted/paths.jou +++ b/self_hosted/paths.jou @@ -9,31 +9,83 @@ declare _mkdir(path: byte*) -> int # windows declare mkdir(path: byte*, mode: int) -> int # posix declare dirname(path: byte*) -> byte* declare stat(path: byte*, buf: byte[1000]*) -> int # lol +declare popen(command: byte*, type: byte*) -> FILE* +declare pclose(stream: FILE*) -> int +declare _NSGetExecutablePath(buf: byte*, bufsize: int*) -> int + def is_windows() -> bool: # TODO: this is just weird... return getenv("OS") != NULL and strstr(getenv("OS"), "Windows") != NULL -# Return a path to the currently running program. Return value must be free()d. -def find_current_executable() -> byte*: +# https://stackoverflow.com/a/3466183 +def is_macos() -> bool: + if is_windows(): + return False + + uname = popen("uname", "r") + if uname == NULL: + return False + + output: byte[100] + memset(&output, 0, sizeof(output)) + fgets(output, sizeof(output) as int, uname) + + pclose(uname) + return starts_with(output, "Darwin") + + +def _find_current_executable_windows() -> byte*: buf = NULL for size = 2L; True; size *= 2: buf = realloc(buf, size) memset(buf, 0, size) + ret = GetModuleFileNameA(NULL, buf, size as int) + if ret <= 0: + return NULL # error --> give up + if ret < size: + # buffer is big enough, it fits + return buf - if is_windows(): - ret: long = GetModuleFileNameA(NULL, buf, size as int) - else: - ret = readlink("/proc/self/exe", buf, size) +def _find_current_executable_macos() -> byte*: + n = 1 + result: byte* = malloc(n) + ret = _NSGetExecutablePath(result, &n) # sets n to desired size + assert ret < 0 # didn't fit + result = realloc(result, n) + ret = _NSGetExecutablePath(result, &n) + assert ret == 0 + return result +def _find_current_executable_linux() -> byte*: + buf = NULL + for size = 2L; True; size *= 2: + buf = realloc(buf, size) + memset(buf, 0, size) + ret = readlink("/proc/self/exe", buf, size) if ret <= 0: - # TODO: include os error message (GetLastError / errno) - fprintf(stderr, "error: cannot locate currently running executable, needed for finding the Jou standard library\n") - exit(1) + return NULL if ret < size: # buffer is big enough, it fits return buf + +def find_current_executable() -> byte*: + if is_windows(): + result = _find_current_executable_windows() + elif is_macos(): + result = _find_current_executable_macos() + else: + result = _find_current_executable_linux() + + if result == NULL: + # TODO: include os error message (GetLastError / errno) + fprintf(stderr, "error: cannot locate currently running executable, needed for finding the Jou standard library\n") + exit(1) + + return result + + def find_installation_directory() -> byte*: exe = find_current_executable() result = strdup(dirname(exe)) diff --git a/src/codegen.c b/src/codegen.c index da3a6509..f638a4d7 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -190,9 +190,11 @@ static LLVMValueRef codegen_function_or_method_decl(const struct State *st, cons // make it a definition instead of a declaration so that there are no linker errors. // Ideally it would be possible to compile some parts of Jou code only for a specific platform. #ifdef _WIN32 - const char *doesnt_exist[] = { "readlink", "mkdir", "popen", "pclose" }; -#else + const char *doesnt_exist[] = { "readlink", "mkdir", "popen", "pclose", "_NSGetExecutablePath" }; +#elif defined(__APPLE__) const char *doesnt_exist[] = { "GetModuleFileNameA", "_mkdir" }; +#else + const char *doesnt_exist[] = { "GetModuleFileNameA", "_mkdir", "_NSGetExecutablePath" }; #endif for (unsigned i = 0; i < sizeof doesnt_exist / sizeof doesnt_exist[0]; i++) { if (!strcmp(fullname, doesnt_exist[i])) {