aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2024-03-08 11:15:13 +0000
committerGitHub <noreply@github.com>2024-03-08 11:15:13 +0000
commit53ce94503422b904ae52990c7d8bab292413b2bc (patch)
treefcaf082b29dee24e80fe5a758e5a14c50ea3f913 /src
parent0e168dd2926dcdb5f1a71001ee0d05e62cbaaca4 (diff)
parent0bb2327d76574c33cfd32f14e11ffd50170b4b9f (diff)
Merge pull request #3230 from avanspector/haiku
Add Haiku OS support
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp71
-rw-r--r--src/check_builtin.cpp1
-rw-r--r--src/checker.cpp1
-rw-r--r--src/gb/gb.h49
-rw-r--r--src/linker.cpp4
-rw-r--r--src/llvm_backend.cpp4
-rw-r--r--src/path.cpp922
-rw-r--r--src/threading.cpp174
-rw-r--r--src/tilde.cpp1
9 files changed, 756 insertions, 471 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 0bcb9f298..fdaa971f1 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -18,6 +18,7 @@ enum TargetOsKind : u16 {
TargetOs_essence,
TargetOs_freebsd,
TargetOs_openbsd,
+ TargetOs_haiku,
TargetOs_wasi,
TargetOs_js,
@@ -78,6 +79,7 @@ gb_global String target_os_names[TargetOs_COUNT] = {
str_lit("essence"),
str_lit("freebsd"),
str_lit("openbsd"),
+ str_lit("haiku"),
str_lit("wasi"),
str_lit("js"),
@@ -542,6 +544,13 @@ gb_global TargetMetrics target_openbsd_amd64 = {
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_haiku_amd64 = {
+ TargetOs_haiku,
+ TargetArch_amd64,
+ 8, 8, 8, 16,
+ str_lit("x86_64-unknown-haiku"),
+};
+
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -641,6 +650,7 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
+ { str_lit("haiku_amd64"), &target_haiku_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
@@ -872,6 +882,58 @@ gb_internal String internal_odin_root_dir(void) {
return path;
}
+#elif defined(GB_SYSTEM_HAIKU)
+
+#include <FindDirectory.h>
+
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
+
+gb_internal String internal_odin_root_dir(void) {
+ String path = global_module_path;
+ isize len, i;
+ u8 *text;
+
+ if (global_module_path_set) {
+ return global_module_path;
+ }
+
+ auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
+
+ len = 0;
+ for (;;) {
+ u32 sz = path_buf.count;
+ int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz);
+ if(res == B_OK) {
+ len = sz;
+ break;
+ } else {
+ array_resize(&path_buf, sz + 1);
+ }
+ }
+
+ mutex_lock(&string_buffer_mutex);
+ defer (mutex_unlock(&string_buffer_mutex));
+
+ text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ gb_memmove(text, &path_buf[0], len);
+
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
+
+ for (i = path.len-1; i >= 0; i--) {
+ u8 c = path[i];
+ if (c == '/' || c == '\\') {
+ break;
+ }
+ path.len--;
+ }
+
+ global_module_path = path;
+ global_module_path_set = true;
+
+ return path;
+}
+
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
@@ -888,6 +950,7 @@ gb_internal String internal_odin_root_dir(void) {
}
auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
len = 0;
for (;;) {
@@ -920,9 +983,6 @@ gb_internal String internal_odin_root_dir(void) {
global_module_path = path;
global_module_path_set = true;
-
- // array_free(&path_buf);
-
return path;
}
#else
@@ -1301,6 +1361,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
metrics = &target_freebsd_amd64;
#elif defined(GB_SYSTEM_OPENBSD)
metrics = &target_openbsd_amd64;
+ #elif defined(GB_SYSTEM_HAIKU)
+ metrics = &target_haiku_amd64;
#elif defined(GB_CPU_ARM)
metrics = &target_linux_arm64;
#else
@@ -1405,6 +1467,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
case TargetOs_openbsd:
bc->link_flags = str_lit("-arch x86-64 ");
break;
+ case TargetOs_haiku:
+ bc->link_flags = str_lit("-arch x86-64 ");
+ break;
}
} else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index c85fb28d6..e1b1cd693 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -4928,6 +4928,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
switch (build_context.metrics.arch) {
case TargetArch_i386:
case TargetArch_amd64:
diff --git a/src/checker.cpp b/src/checker.cpp
index 5827fc695..72c0ae574 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1010,6 +1010,7 @@ gb_internal void init_universal(void) {
{"Linux", TargetOs_linux},
{"Essence", TargetOs_essence},
{"FreeBSD", TargetOs_freebsd},
+ {"Haiku", TargetOs_haiku},
{"OpenBSD", TargetOs_openbsd},
{"WASI", TargetOs_wasi},
{"JS", TargetOs_js},
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 93d250f21..702647121 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -83,6 +83,10 @@ extern "C" {
#ifndef GB_SYSTEM_OPENBSD
#define GB_SYSTEM_OPENBSD 1
#endif
+ #elif defined(__HAIKU__) || defined(__haiku__)
+ #ifndef GB_SYSTEM_HAIKU
+ #define GB_SYSTEM_HAIKU 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -206,7 +210,7 @@ extern "C" {
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__)
#include <sys/sendfile.h>
#endif
#include <sys/stat.h>
@@ -247,6 +251,13 @@ extern "C" {
#include <pthread_np.h>
#define lseek64 lseek
#endif
+
+#if defined(GB_SYSTEM_HAIKU)
+ #include <stdio.h>
+ #include <pthread.h>
+ #include <kernel/OS.h>
+ #define lseek64 lseek
+#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
@@ -801,6 +812,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_HAIKU)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -2984,6 +3002,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3184,7 +3204,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
//info.affinity_tag = cast(integer_t)index;
//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
+#if !defined(GB_SYSTEM_HAIKU)
result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
+#endif
return result == 0;
}
@@ -3240,6 +3262,29 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
}
+#elif defined(GB_SYSTEM_HAIKU)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
#else
#error TODO(bill): Unknown system
#endif
@@ -5457,7 +5502,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
}
}
- gb_free(buf);
+ gb_mfree(buf);
close(new_fd);
close(existing_fd);
diff --git a/src/linker.cpp b/src/linker.cpp
index 0144c4aaf..0cdeaf8d9 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -474,8 +474,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
- } else if (build_context.metrics.os != TargetOs_openbsd) {
- // OpenBSD defaults to PIE executable. do not pass -no-pie for it.
+ } else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku) {
+ // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index efba19f23..ca4341525 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -2564,8 +2564,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
switch (build_context.reloc_mode) {
case RelocMode_Default:
- if (build_context.metrics.os == TargetOs_openbsd) {
- // Always use PIC for OpenBSD: it defaults to PIE
+ if (build_context.metrics.os == TargetOs_openbsd || build_context.metrics.os == TargetOs_haiku) {
+ // Always use PIC for OpenBSD and Haiku: they default to PIE
reloc_mode = LLVMRelocPIC;
}
break;
diff --git a/src/path.cpp b/src/path.cpp
index de80c9def..742bba7f8 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -1,461 +1,461 @@
-/*
- Path handling utilities.
-*/
-#if !defined(GB_SYSTEM_WINDOWS)
-#include <unistd.h>
-#endif
-
-gb_internal String remove_extension_from_path(String const &s) {
- if (s.len != 0 && s.text[s.len-1] == '.') {
- return s;
- }
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '.') {
- return substring(s, 0, i);
- }
- }
- return s;
-}
-
-gb_internal String remove_directory_from_path(String const &s) {
- isize len = 0;
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- len += 1;
- }
- return substring(s, s.len-len, s.len);
-}
-
-
-// NOTE(Mark Naughton): getcwd as String
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal String get_current_directory(void) {
- char cwd[256];
- getcwd(cwd, 256);
-
- return make_string_c(cwd);
-}
-
-#else
-gb_internal String get_current_directory(void) {
- gbAllocator a = heap_allocator();
-
- wchar_t cwd[256];
- GetCurrentDirectoryW(256, cwd);
-
- String16 wstr = make_string16_c(cwd);
-
- return string16_to_string(a, wstr);
-}
-#endif
-
-gb_internal bool path_is_directory(String path);
-
-gb_internal String directory_from_path(String const &s) {
- if (path_is_directory(s)) {
- return s;
- }
-
- isize i = s.len-1;
- for (; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- }
- if (i >= 0) {
- return substring(s, 0, i);
- }
- return substring(s, 0, 0);
-}
-
-#if defined(GB_SYSTEM_WINDOWS)
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-gb_internal String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return copy_string(a, res);
-}
-
-struct Path {
- String basename;
- String name;
- String ext;
-};
-
-// NOTE(Jeroen): Naively turns a Path into a string.
-gb_internal String path_to_string(gbAllocator a, Path path) {
- if (path.basename.len + path.name.len + path.ext.len == 0) {
- return make_string(nullptr, 0);
- }
-
- isize len = path.basename.len + 1 + path.name.len + 1;
- if (path.ext.len > 0) {
- len += path.ext.len + 1;
- }
-
- u8 *str = gb_alloc_array(a, u8, len);
-
- isize i = 0;
- gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
-
- gb_memmove(str+i, "/", 1); i += 1;
-
- gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
- if (path.ext.len > 0) {
- gb_memmove(str+i, ".", 1); i += 1;
- gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
- }
- str[i] = 0;
-
- String res = make_string(str, i);
- res = string_trim_whitespace(res);
- return res;
-}
-
-// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
-gb_internal String path_to_full_path(gbAllocator a, Path path) {
- String temp = path_to_string(heap_allocator(), path);
- defer (gb_free(heap_allocator(), temp.text));
-
- return path_to_full_path(a, temp);
-}
-
-// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
-// and then breaks it into its components to make a Path.
-gb_internal Path path_from_string(gbAllocator a, String const &path) {
- Path res = {};
-
- if (path.len == 0) return res;
-
- String fullpath = path_to_full_path(a, path);
- defer (gb_free(heap_allocator(), fullpath.text));
-
- res.basename = directory_from_path(fullpath);
- res.basename = copy_string(a, res.basename);
-
- if (path_is_directory(fullpath)) {
- // It's a directory. We don't need to tinker with the name and extension.
- // It could have a superfluous trailing `/`. Remove it if so.
- if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
- res.basename.len--;
- }
- return res;
- }
-
- // Note(Dragos): Is the copy_string required if it's a substring?
- isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
- res.name = substring(fullpath, name_start, fullpath.len);
- res.name = remove_extension_from_path(res.name);
- res.name = copy_string(a, res.name);
-
- res.ext = path_extension(fullpath, false); // false says not to include the dot.
- res.ext = copy_string(a, res.ext);
- return res;
-}
-
-// NOTE(Jeroen): Takes a path String and returns the last path element.
-gb_internal String last_path_element(String const &path) {
- isize count = 0;
- u8 * start = (u8 *)(&path.text[path.len - 1]);
- for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
- count++;
- start--;
- }
- if (count > 0) {
- start++; // Advance past the `/` and return the substring.
- String res = make_string(start, count);
- return res;
- }
- // Must be a root path like `/` or `C:/`, return empty String.
- return STR_LIT("");
-}
-
-gb_internal bool path_is_directory(Path path) {
- String path_string = path_to_full_path(heap_allocator(), path);
- defer (gb_free(heap_allocator(), path_string.text));
-
- return path_is_directory(path_string);
-}
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-gb_internal i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(temporary_allocator(), path);
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- gbAllocator a = heap_allocator();
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- wchar_t *filename_w = file_data.cFileName;
- u64 size = cast(u64)file_data.nFileSizeLow;
- size |= (cast(u64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = cast(i64)size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
-
-#include <dirent.h>
-
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-
-
-#else
-#error Implement read_directory
-#endif
-
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal bool write_directory(String path) {
- char const *pathname = (char *) path.text;
-
- if (access(pathname, W_OK) < 0) {
- return false;
- }
-
- return true;
-}
-#else
-gb_internal bool write_directory(String path) {
- String16 wstr = string_to_string16(heap_allocator(), path);
- LPCWSTR wdirectory_name = wstr.text;
-
- HANDLE directory = CreateFileW(wdirectory_name,
- GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- if (directory == INVALID_HANDLE_VALUE) {
- DWORD error_code = GetLastError();
- if (error_code == ERROR_ACCESS_DENIED) {
- return false;
- }
- }
-
- CloseHandle(directory);
- return true;
-}
-#endif
+/*
+ Path handling utilities.
+*/
+#if !defined(GB_SYSTEM_WINDOWS)
+#include <unistd.h>
+#endif
+
+gb_internal String remove_extension_from_path(String const &s) {
+ if (s.len != 0 && s.text[s.len-1] == '.') {
+ return s;
+ }
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '.') {
+ return substring(s, 0, i);
+ }
+ }
+ return s;
+}
+
+gb_internal String remove_directory_from_path(String const &s) {
+ isize len = 0;
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ len += 1;
+ }
+ return substring(s, s.len-len, s.len);
+}
+
+
+// NOTE(Mark Naughton): getcwd as String
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal String get_current_directory(void) {
+ char cwd[256];
+ getcwd(cwd, 256);
+
+ return make_string_c(cwd);
+}
+
+#else
+gb_internal String get_current_directory(void) {
+ gbAllocator a = heap_allocator();
+
+ wchar_t cwd[256];
+ GetCurrentDirectoryW(256, cwd);
+
+ String16 wstr = make_string16_c(cwd);
+
+ return string16_to_string(a, wstr);
+}
+#endif
+
+gb_internal bool path_is_directory(String path);
+
+gb_internal String directory_from_path(String const &s) {
+ if (path_is_directory(s)) {
+ return s;
+ }
+
+ isize i = s.len-1;
+ for (; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ }
+ if (i >= 0) {
+ return substring(s, 0, i);
+ }
+ return substring(s, 0, 0);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ String16 wstr = string_to_string16(a, path);
+ defer (gb_free(a, wstr.text));
+
+ i32 attribs = GetFileAttributesW(wstr.text);
+ if (attribs < 0) return false;
+
+ return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+#else
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ char *copy = cast(char *)copy_string(a, path).text;
+ defer (gb_free(a, copy));
+
+ struct stat s;
+ if (stat(copy, &s) == 0) {
+ return (s.st_mode & S_IFDIR) != 0;
+ }
+ return false;
+ }
+#endif
+
+
+gb_internal String path_to_full_path(gbAllocator a, String path) {
+ gbAllocator ha = heap_allocator();
+ char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
+ defer (gb_free(ha, path_c));
+
+ char *fullpath = gb_path_get_full_name(a, path_c);
+ String res = string_trim_whitespace(make_string_c(fullpath));
+#if defined(GB_SYSTEM_WINDOWS)
+ for (isize i = 0; i < res.len; i++) {
+ if (res.text[i] == '\\') {
+ res.text[i] = '/';
+ }
+ }
+#endif
+ return copy_string(a, res);
+}
+
+struct Path {
+ String basename;
+ String name;
+ String ext;
+};
+
+// NOTE(Jeroen): Naively turns a Path into a string.
+gb_internal String path_to_string(gbAllocator a, Path path) {
+ if (path.basename.len + path.name.len + path.ext.len == 0) {
+ return make_string(nullptr, 0);
+ }
+
+ isize len = path.basename.len + 1 + path.name.len + 1;
+ if (path.ext.len > 0) {
+ len += path.ext.len + 1;
+ }
+
+ u8 *str = gb_alloc_array(a, u8, len);
+
+ isize i = 0;
+ gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+
+ gb_memmove(str+i, "/", 1); i += 1;
+
+ gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
+ if (path.ext.len > 0) {
+ gb_memmove(str+i, ".", 1); i += 1;
+ gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
+ }
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return res;
+}
+
+// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
+gb_internal String path_to_full_path(gbAllocator a, Path path) {
+ String temp = path_to_string(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), temp.text));
+
+ return path_to_full_path(a, temp);
+}
+
+// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
+// and then breaks it into its components to make a Path.
+gb_internal Path path_from_string(gbAllocator a, String const &path) {
+ Path res = {};
+
+ if (path.len == 0) return res;
+
+ String fullpath = path_to_full_path(a, path);
+ defer (gb_free(heap_allocator(), fullpath.text));
+
+ res.basename = directory_from_path(fullpath);
+ res.basename = copy_string(a, res.basename);
+
+ if (path_is_directory(fullpath)) {
+ // It's a directory. We don't need to tinker with the name and extension.
+ // It could have a superfluous trailing `/`. Remove it if so.
+ if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+ res.basename.len--;
+ }
+ return res;
+ }
+
+ // Note(Dragos): Is the copy_string required if it's a substring?
+ isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+ res.name = substring(fullpath, name_start, fullpath.len);
+ res.name = remove_extension_from_path(res.name);
+ res.name = copy_string(a, res.name);
+
+ res.ext = path_extension(fullpath, false); // false says not to include the dot.
+ res.ext = copy_string(a, res.ext);
+ return res;
+}
+
+// NOTE(Jeroen): Takes a path String and returns the last path element.
+gb_internal String last_path_element(String const &path) {
+ isize count = 0;
+ u8 * start = (u8 *)(&path.text[path.len - 1]);
+ for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
+ count++;
+ start--;
+ }
+ if (count > 0) {
+ start++; // Advance past the `/` and return the substring.
+ String res = make_string(start, count);
+ return res;
+ }
+ // Must be a root path like `/` or `C:/`, return empty String.
+ return STR_LIT("");
+}
+
+gb_internal bool path_is_directory(Path path) {
+ String path_string = path_to_full_path(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), path_string.text));
+
+ return path_is_directory(path_string);
+}
+
+struct FileInfo {
+ String name;
+ String fullpath;
+ i64 size;
+ bool is_dir;
+};
+
+enum ReadDirectoryError {
+ ReadDirectory_None,
+
+ ReadDirectory_InvalidPath,
+ ReadDirectory_NotExists,
+ ReadDirectory_Permission,
+ ReadDirectory_NotDir,
+ ReadDirectory_Empty,
+ ReadDirectory_Unknown,
+
+ ReadDirectory_COUNT,
+};
+
+gb_internal i64 get_file_size(String path) {
+ char *c_str = alloc_cstring(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), c_str));
+
+ gbFile f = {};
+ gbFileError err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+ if (err != gbFileError_None) {
+ return -1;
+ }
+ return gb_file_size(&f);
+}
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+
+ while (path.len > 0) {
+ Rune end = path[path.len-1];
+ if (end == '/') {
+ path.len -= 1;
+ } else if (end == '\\') {
+ path.len -= 1;
+ } else {
+ break;
+ }
+ }
+
+ if (path.len == 0) {
+ return ReadDirectory_InvalidPath;
+ }
+ {
+ char *c_str = alloc_cstring(temporary_allocator(), path);
+ gbFile f = {};
+ gbFileError file_err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+
+ switch (file_err) {
+ case gbFileError_Invalid: return ReadDirectory_InvalidPath;
+ case gbFileError_NotExists: return ReadDirectory_NotExists;
+ // case gbFileError_Permission: return ReadDirectory_Permission;
+ }
+ }
+
+ if (!path_is_directory(path)) {
+ return ReadDirectory_NotDir;
+ }
+
+
+ gbAllocator a = heap_allocator();
+ char *new_path = gb_alloc_array(a, char, path.len+3);
+ defer (gb_free(a, new_path));
+
+ gb_memmove(new_path, path.text, path.len);
+ gb_memmove(new_path+path.len, "/*", 2);
+ new_path[path.len+2] = 0;
+
+ String np = make_string(cast(u8 *)new_path, path.len+2);
+ String16 wstr = string_to_string16(a, np);
+ defer (gb_free(a, wstr.text));
+
+ WIN32_FIND_DATAW file_data = {};
+ HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+ if (find_file == INVALID_HANDLE_VALUE) {
+ return ReadDirectory_Unknown;
+ }
+ defer (FindClose(find_file));
+
+ array_init(fi, a, 0, 100);
+
+ do {
+ wchar_t *filename_w = file_data.cFileName;
+ u64 size = cast(u64)file_data.nFileSizeLow;
+ size |= (cast(u64)file_data.nFileSizeHigh) << 32;
+ String name = string16_to_string(a, make_string16_c(filename_w));
+ if (name == "." || name == "..") {
+ gb_free(a, name.text);
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = cast(i64)size;
+ info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ array_add(fi, info);
+ } while (FindNextFileW(find_file, &file_data));
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU)
+
+#include <dirent.h>
+
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ char *c_path = alloc_cstring(a, path);
+ defer (gb_free(a, c_path));
+
+ DIR *dir = opendir(c_path);
+ if (!dir) {
+ switch (errno) {
+ case ENOENT:
+ return ReadDirectory_NotExists;
+ case EACCES:
+ return ReadDirectory_Permission;
+ case ENOTDIR:
+ return ReadDirectory_NotDir;
+ default:
+ // ENOMEM: out of memory
+ // EMFILE: per-process limit on open fds reached
+ // ENFILE: system-wide limit on total open files reached
+ return ReadDirectory_Unknown;
+ }
+ GB_PANIC("unreachable");
+ }
+
+ array_init(fi, a, 0, 100);
+
+ for (;;) {
+ struct dirent *entry = readdir(dir);
+ if (entry == nullptr) {
+ break;
+ }
+
+ String name = make_string_c(entry->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+ filepath.text[filepath.len] = 0;
+
+
+ struct stat dir_stat = {};
+
+ if (stat((char *)filepath.text, &dir_stat)) {
+ continue;
+ }
+
+ if (S_ISDIR(dir_stat.st_mode)) {
+ continue;
+ }
+
+ i64 size = dir_stat.st_size;
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = size;
+ array_add(fi, info);
+ }
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+
+
+#else
+#error Implement read_directory
+#endif
+
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal bool write_directory(String path) {
+ char const *pathname = (char *) path.text;
+
+ if (access(pathname, W_OK) < 0) {
+ return false;
+ }
+
+ return true;
+}
+#else
+gb_internal bool write_directory(String path) {
+ String16 wstr = string_to_string16(heap_allocator(), path);
+ LPCWSTR wdirectory_name = wstr.text;
+
+ HANDLE directory = CreateFileW(wdirectory_name,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (directory == INVALID_HANDLE_VALUE) {
+ DWORD error_code = GetLastError();
+ if (error_code == ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ CloseHandle(directory);
+ return true;
+}
+#endif
diff --git a/src/threading.cpp b/src/threading.cpp
index 725b58c89..a469435d2 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -492,6 +492,8 @@ gb_internal u32 thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
#else
#error Unsupported architecture for thread_current_id()
#endif
@@ -831,8 +833,178 @@ gb_internal void futex_wait(Futex *f, Footex val) {
WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
} while (f->load() == val);
}
+
+#elif defined(GB_SYSTEM_HAIKU)
+
+// Futex implementation taken from https://tavianator.com/2023/futex.html
+
+#include <pthread.h>
+#include <atomic>
+
+struct _Spinlock {
+ std::atomic_flag state;
+
+ void init() {
+ state.clear();
+ }
+
+ void lock() {
+ while (state.test_and_set(std::memory_order_acquire)) {
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+ }
+
+ void unlock() {
+ state.clear(std::memory_order_release);
+ }
+};
+
+struct Futex_Waitq;
+
+struct Futex_Waiter {
+ _Spinlock lock;
+ pthread_t thread;
+ Futex *futex;
+ Futex_Waitq *waitq;
+ Futex_Waiter *prev, *next;
+};
+
+struct Futex_Waitq {
+ _Spinlock lock;
+ Futex_Waiter list;
+
+ void init() {
+ auto head = &list;
+ head->prev = head->next = head;
+ }
+};
+
+// FIXME: This approach may scale badly in the future,
+// possible solution - hash map (leads to deadlocks now).
+
+Futex_Waitq g_waitq = {
+ .lock = ATOMIC_FLAG_INIT,
+ .list = {
+ .prev = &g_waitq.list,
+ .next = &g_waitq.list,
+ },
+};
+
+Futex_Waitq *get_waitq(Futex *f) {
+ // Future hash map method...
+ return &g_waitq;
+}
+
+void futex_signal(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_broadcast(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ if (waiter->next == head) {
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ } else {
+ pthread_kill(waiter->thread, SIGCONT);
+ }
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_wait(Futex *f, Footex val) {
+ Futex_Waiter waiter;
+ waiter.thread = pthread_self();
+ waiter.futex = f;
+
+ auto waitq = get_waitq(f);
+ while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
+ if (f->load(std::memory_order_relaxed) != val) {
+ return;
+ }
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+
+ waiter.waitq = waitq;
+ waiter.lock.init();
+ waiter.lock.lock();
+
+ auto head = &waitq->list;
+ waiter.prev = head->prev;
+ waiter.next = head;
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ sigset_t old_mask, mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCONT);
+ pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
+
+ if (f->load(std::memory_order_relaxed) == val) {
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+
+ int sig;
+ sigwait(&mask, &sig);
+
+ waitq->lock.lock();
+ waiter.lock.lock();
+
+ while (waitq != waiter.waitq) {
+ auto req = waiter.waitq;
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+ waitq = req;
+ waitq->lock.lock();
+ waiter.lock.lock();
+ }
+ }
+
+ waiter.prev->next = waiter.next;
+ waiter.next->prev = waiter.prev;
+
+ pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
+
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+}
+
#endif
#if defined(GB_SYSTEM_WINDOWS)
#pragma warning(pop)
-#endif \ No newline at end of file
+#endif
diff --git a/src/tilde.cpp b/src/tilde.cpp
index 06428f317..4fc7d1c9b 100644
--- a/src/tilde.cpp
+++ b/src/tilde.cpp
@@ -825,6 +825,7 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
debug_format = TB_DEBUGFMT_DWARF;
break;
}