#ifndef GEBS_H_ #define GEBS_H_ // ---------------------------------------------------------------------------- // Platform macros // ---------------------------------------------------------------------------- #define GEBS_PLATFORM_POSIX 0 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) # define GEBS_PLATFORM GEBS_PLATFORM_POSIX #endif #ifndef GEBS_PLATFORM # error "GEBS_PLATFORM is undefined!" #endif // ---------------------------------------------------------------------------- // Includes // ---------------------------------------------------------------------------- #include #include #include #include #include #include #include #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX # include # include # include # include # include #else # error "Unknown GEBS_PLATFORM" #endif #define discard __attribute__((unused)) // ---------------------------------------------------------------------------- // Parsing commandline arguments // ---------------------------------------------------------------------------- #define GEBS_SHIFT_CHECK(argc) \ (*(argc) <= 0 ? (GEBS_LOGE("Expected more arguments\n"), exit(EXIT_FAILURE)) : (0)) #define GEBS_SHIFT(argc, argv) \ (GEBS_SHIFT_CHECK((argc)), --(*(argc)), *(*(argv))++) // ---------------------------------------------------------------------------- // Defer macro // ---------------------------------------------------------------------------- #define defer defer__2(__COUNTER__) #define defer__2(X) defer__3(X) #define defer__3(X) defer__4(defer__id##X) #define defer__4(ID) auto void ID##func(char (*)[]); __attribute__((cleanup(ID##func))) char ID##var[0]; void ID##func(char (*ID##param)[]) // ---------------------------------------------------------------------------- // Logging // ---------------------------------------------------------------------------- #define GEBS_LOGI(fmt, ...) fprintf(stdout, "Info: "fmt, ##__VA_ARGS__) #define GEBS_LOGE(fmt, ...) fprintf(stdout, "Error: "fmt, ##__VA_ARGS__) #define GEBS_LOGD(fmt, ...) fprintf(stdout, "Debug: "fmt, ##__VA_ARGS__) #define GEBS_LOGW(fmt, ...) fprintf(stdout, "Warning: "fmt, ##__VA_ARGS__) #ifdef GEBS_NO_FS_OPERATION_LOGGING # define GEBS_LOGO(fmt, ...) #else # define GEBS_LOGO(fmt, ...) fprintf(stdout, "Info: "fmt, ##__VA_ARGS__) #endif // ---------------------------------------------------------------------------- // Allocators // ---------------------------------------------------------------------------- typedef struct { void *(*malloc)(void *self, size_t size); void (*free)(void *self, void *memory); void *(*realloc)(void *self, void *memory, size_t prev_size, size_t new_size); } Gebs_Allocator; // Wrapper macros #define gebs_malloc(alloc, size) ((alloc)->malloc((void *)(alloc), (size))) #define gebs_free(alloc, memory) ((alloc)->free((void *)(alloc), (memory))) #define gebs_realloc(alloc, memory, prev_size, new_size) \ ((alloc)->realloc((void *)(alloc), (memory), (prev_size), (new_size))) extern Gebs_Allocator gebs_default_allocator; void *gebs_default_allocator_malloc(void *self, size_t size); void gebs_default_allocator_free(void *self, void *memory); void *gebs_default_allocator_realloc(void *self, void *memory, size_t prev_size, size_t new_size); typedef struct { void *(*malloc)(void *self, size_t size); void (*free)(void *self, void *memory); void *(*realloc)(void *self, void *memory, size_t prev_size, size_t new_size); void *buffer; void *end; size_t capacity; } Gebs_Arena; void gebs_arena_reset(Gebs_Arena *a); void gebs_arena_destroy(Gebs_Arena *a); void *gebs_arena_malloc(void *self, size_t size); void gebs_arena_free(void *self, void *memory); void *gebs_arena_realloc(void *self, void *memory, size_t prev_size, size_t new_size); #define gebs_arena_get(cap) \ ({ \ Gebs_Arena __arena = {0}; \ __arena.malloc = &gebs_arena_malloc; \ __arena.realloc = &gebs_arena_realloc; \ __arena.free = &gebs_arena_free; \ __arena.capacity = (cap); \ __arena; \ }) // ---------------------------------------------------------------------------- // Dynamic list // ---------------------------------------------------------------------------- #define gebs_list_append_alloc(alloc, list, item) \ do { \ if ((list)->items == NULL) { \ (list)->capacity = 1; \ (list)->items = gebs_malloc((alloc), \ sizeof(*(list)->items) * (list)->capacity); \ } else { \ if ((list)->count == (list)->capacity) { \ size_t __prev_capacity = (list)->capacity; \ (list)->capacity *= 2; \ (list)->items = gebs_realloc((alloc), (list)->items, \ sizeof(*(list)->items) * __prev_capacity, \ sizeof(*(list)->items) * (list)->capacity); \ } \ } \ (list)->items[(list)->count++] = (item); \ } while(0) #define gebs_list_append(list, item) \ gebs_list_append_alloc(&gebs_default_allocator, (list), (item)) #define gebs_list_free_alloc(alloc, list) \ do { \ if ((list)->items != NULL) { \ gebs_free((alloc), (list)->items); \ (list)->items = NULL; \ } \ (list)->count = 0; \ } while(0) #define gebs_list_free(list) gebs_list_free_alloc(&gebs_default_allocator, (list)) // List of null-terminated strings typedef struct { char **items; size_t count, capacity; } Gebs_NString_List; #define gebs_nsl_join_alloc(alloc, nsl, sb, sep) \ do { \ for (size_t __i = 0; __i < (nsl)->count; __i++) { \ gebs_sb_append_nstr_alloc((alloc), (sb), (nsl)->items[__i]); \ if (__i != (nsl)->count - 1) { \ gebs_sb_append_nstr_alloc((alloc), (sb), (sep)); \ } \ } \ } while(0) typedef struct { char *items; size_t count, capacity; } Gebs_String_Builder; #define gebs_sb_append_char_alloc gebs_list_append_alloc #define gebs_sb_free_alloc gebs_list_free_alloc #define gebs_sb_finish_alloc(alloc, sb) \ gebs_sb_append_char_alloc((alloc), (sb), '\0') #define gebs_sb_append_nstr_alloc(alloc, sb, nstr) \ do { \ char *__p = (nstr); \ for (size_t __i = 0; __p[__i]; __i++) { \ gebs_sb_append_char_alloc((alloc), (sb), __p[__i]); \ } \ } while(0) #define gebs_sb_append_char(sb, chr) \ gebs_sb_append_char_alloc(&gebs_default_allocator, (sb), (chr)) #define gebs_sb_free(sb) \ gebs_sb_free_alloc(&gebs_default_allocator, (sb)) #define gebs_sb_finish(sb) \ gebs_sb_finish_alloc(&gebs_default_allocator, (sb)) #define gebs_sb_append_nstr(sb, nstr) \ gebs_sb_append_nstr_alloc(&gebs_default_allocator, (sb), (nstr)) // ---------------------------------------------------------------------------- // Filesystem operations // ---------------------------------------------------------------------------- bool gebs_rename(const char *a, const char *b); bool gebs_remove(const char *p); bool gebs_mkdir(const char *p); bool gebs_exists(const char *p); // ---------------------------------------------------------------------------- // Commands // ---------------------------------------------------------------------------- typedef Gebs_NString_List Gebs_Cmd; #define gebs_cmd_append_alloc gebs_list_append_alloc #define gebs_cmd_free_alloc gebs_list_free_alloc #define gebs_cmd_append gebs_list_append #define gebs_cmd_free gebs_list_free #define gebs_cmd_run_alloc gebs_cmd_run_sync_alloc #define gebs_cmd_run(cmd) gebs_cmd_run_alloc(&gebs_default_allocator, (cmd)) int gebs_cmd_run_sync_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd); #define GEBS_CMD(...) \ ({ \ char *__args[] = { __VA_ARGS__ }; \ Gebs_Cmd __cmd = {0}; \ defer { gebs_cmd_free(&__cmd); } \ for (size_t __i = 0; __i < sizeof(__args)/sizeof(__args[0]); __i++) { \ gebs_cmd_append(&__cmd, __args[__i]); \ } \ gebs_cmd_run(&__cmd); \ }) int gebs_cmd_run_sync_collect_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd, Gebs_String_Builder *sb_out); #define gebs_cmd_run_collect_alloc gebs_cmd_run_sync_collect_alloc #define gebs_cmd_run_collect(cmd, out) \ gebs_cmd_run_collect_alloc(&gebs_default_allocator, (cmd), (out)) // ---------------------------------------------------------------------------- // The build system // ---------------------------------------------------------------------------- bool gebs_needs_rebuild(const char *out, const char *in); void gebs_rebuild_self1_alloc(Gebs_Allocator *alloc, int argc, char ** argv, int cmd_argc, char **cmd_argv, char *source); #define gebs_rebuild_self(argc, argv, ...) \ do { \ const char *__argv[] = { __VA_ARGS__ }; \ gebs_rebuild_self1_alloc(&gebs_default_allocator, argc, argv, \ sizeof(__argv)/sizeof(__argv[0]), (char**)__argv, __FILE__); \ } while(0) #define gebs_needs_rebuild_many(out, ...) \ ({ \ const char *__deps[] = { __VA_ARGS__ }; \ bool __ok = false; \ for (size_t __i = 0; __i < sizeof(__deps)/sizeof(__deps[0]); __i++) { \ if (gebs_needs_rebuild((out), __deps[__i])) { \ __ok = true; \ break; \ } \ } \ __ok; \ }) #define gebs_make_compile_flags(...) \ do { \ if (gebs_needs_rebuild("compile_flags.txt", __FILE__)) { \ GEBS_LOGI("Creating compile_flags.txt\n"); \ const char *__flags[] = { __VA_ARGS__ }; \ FILE *__out = fopen("compile_flags.txt", "w"); \ char __nl = '\n'; \ if (__out != NULL) { \ for (size_t __i = 0; __i < sizeof(__flags)/sizeof(__flags[0]); __i++) { \ const char *__flag = __flags[__i]; \ fwrite(__flag, strlen(__flag), 1, __out); \ fwrite(&__nl, 1, 1, __out); \ } \ fclose(__out); \ } \ } \ } while(0) #define GEBS_RULE(target, ...) if (needs_rebuild_many(target, __VA_ARGS__)) // ---------------------------------------------------------------------------- // Scratch arena // ---------------------------------------------------------------------------- #ifndef GEBS_SCRATCH_ARENA_SIZE # define GEBS_SCRATCH_ARENA_SIZE 4096 #endif extern Gebs_Arena gebs_scratch_arena; char *gebs_fmt(const char *fmt, ...); void gebs_scratch_arena_reset(void); #ifdef GEBS_IMPLEMENTATION // ---------------------------------------------------------------------------- // Commands // ---------------------------------------------------------------------------- int gebs_cmd_run_sync_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd) { Gebs_String_Builder sb = {0}; gebs_nsl_join_alloc(alloc, cmd, &sb, " "); gebs_sb_finish_alloc(alloc, &sb); GEBS_LOGI("cmd `%s`\n", sb.items); #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX // Clone the list Gebs_Cmd new_cmd = {0}; defer { gebs_cmd_free_alloc(alloc, &new_cmd); } for (size_t i = 0; i < cmd->count; i++) { gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]); } gebs_cmd_append_alloc((alloc), &new_cmd, NULL); pid_t pid = vfork(); switch (pid) { case -1: GEBS_LOGE("Could not vfork\n"); return -1; case 0: execvp(new_cmd.items[0], (char * const *)new_cmd.items); exit(EXIT_FAILURE); default: { int wstatus; do { if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) { return -1; } if (WIFEXITED(wstatus)) { return WEXITSTATUS(wstatus); } else if (WIFSIGNALED(wstatus)) { GEBS_LOGI("%d killed by %d\n", pid, WTERMSIG(wstatus)); return -1; } else if (WIFSTOPPED(wstatus)) { GEBS_LOGI("%d stopped by %d\n", pid, WSTOPSIG(wstatus)); return -1; } } while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); } } #else # error "gebs_cmd_run_sync_alloc unknown command" #endif } int gebs_cmd_run_sync_collect_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd, Gebs_String_Builder *sb_out) { Gebs_String_Builder sb = {0}; gebs_nsl_join_alloc(alloc, cmd, &sb, " "); gebs_sb_finish_alloc(alloc, &sb); GEBS_LOGI("cmd `%s`\n", sb.items); #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX // Clone the list Gebs_Cmd new_cmd = {0}; defer { gebs_cmd_free_alloc(alloc, &new_cmd); } for (size_t i = 0; i < cmd->count; i++) { gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]); } gebs_cmd_append_alloc((alloc), &new_cmd, NULL); int pipe_ends[2]; pipe(pipe_ends); pid_t pid = vfork(); switch (pid) { case -1: close(pipe_ends[0]); close(pipe_ends[1]); GEBS_LOGE("Could not vfork\n"); return -1; case 0: close(pipe_ends[0]); dup2(pipe_ends[1], 1); close(pipe_ends[1]); execvp(new_cmd.items[0], (char * const *)new_cmd.items); exit(EXIT_FAILURE); default: { close(pipe_ends[1]); char c; while (read(pipe_ends[0], &c, 1) > 0) { gebs_sb_append_char_alloc(alloc, sb_out, c); } gebs_sb_finish_alloc(alloc, sb_out); int wstatus; do { if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) { return -1; } if (WIFEXITED(wstatus)) { return WEXITSTATUS(wstatus); } else if (WIFSIGNALED(wstatus)) { GEBS_LOGI("%d killed by %d\n", pid, WTERMSIG(wstatus)); return -1; } else if (WIFSTOPPED(wstatus)) { GEBS_LOGI("%d stopped by %d\n", pid, WSTOPSIG(wstatus)); return -1; } } while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus)); } } #else # error "gebs_cmd_run_sync_alloc unknown command" #endif } // ---------------------------------------------------------------------------- // Allocators // ---------------------------------------------------------------------------- Gebs_Allocator gebs_default_allocator = { .malloc = &gebs_default_allocator_malloc, .free = &gebs_default_allocator_free, .realloc = &gebs_default_allocator_realloc, }; void *gebs_default_allocator_malloc(discard void *self, size_t size) { return malloc(size); } void gebs_default_allocator_free(discard void *self, void *memory) { free(memory); } void *gebs_default_allocator_realloc(discard void *self, void *memory, discard size_t prev_size, size_t new_size) { void *p = realloc(memory, new_size); return p; } void *gebs_arena_malloc(void *self, size_t size) { Gebs_Arena *a = (Gebs_Arena *)self; if (a->buffer == NULL) { #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX int prot = PROT_READ | PROT_WRITE; int vis = MAP_SHARED | MAP_ANONYMOUS; a->buffer = mmap(0, a->capacity, prot, vis, -1, 0); #else # error "gebs_arena_malloc unknown platform" #endif a->end = a->buffer; } void *p = a->end; a->end = (void *)((uintptr_t)a->end + size); return p; } void gebs_arena_free(discard void *self, discard void *memory) { } void *gebs_arena_realloc(void *self, void *memory, size_t prev_size, size_t new_size) { Gebs_Arena *a = (Gebs_Arena *)self; void *new = gebs_arena_malloc(a, new_size); memcpy(new, memory, prev_size); return new; } void gebs_arena_reset(Gebs_Arena *a) { a->end = a->buffer; } void gebs_arena_destroy(Gebs_Arena *a) { #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX munmap(a->buffer, a->capacity); #else # error "gebs_arena_destroy unknown platform" #endif } // ---------------------------------------------------------------------------- // Scratch arena // ---------------------------------------------------------------------------- Gebs_Arena gebs_scratch_arena; __attribute__((constructor)) void gebs_scratch_areana_init(void) { gebs_scratch_arena = gebs_arena_get(4*1024); } __attribute__((destructor)) void gebs_scratch_areana_free(void) { gebs_arena_destroy(&gebs_scratch_arena); } char *gebs_fmt(const char *fmt, ...) { va_list list; va_start(list, fmt); va_list list_copy; va_copy(list_copy, list); size_t size = vsnprintf(NULL, 0, fmt, list_copy); va_end(list_copy); char *buf = gebs_malloc(&gebs_scratch_arena, size+1); vsprintf(buf, fmt, list); va_end(list); return buf; } void gebs_scratch_arena_reset(void) { gebs_arena_reset(&gebs_scratch_arena); } // ---------------------------------------------------------------------------- // Filesystem operations // ---------------------------------------------------------------------------- bool gebs_mkdir(const char *p) { #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX return mkdir(p, 0777) == 0; #else # error "gebs_mkdir unknown platform" #endif } bool gebs_exists(const char *p) { #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX struct stat st; return stat(p, &st) == 0; #else # error "gebs_exists unknown platform" #endif } bool gebs_rename(const char *a, const char *b) { GEBS_LOGO("rename %s -> %s\n", a, b); #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX return rename(a, b) == 0; #else # error "gebs_rename unknown platform" #endif } bool gebs_remove(const char *p) { GEBS_LOGO("remove %s\n", p); #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX return remove(p) == 0; #else # error "gebs_remove unknown platform" #endif } // ---------------------------------------------------------------------------- // The build system // ---------------------------------------------------------------------------- bool gebs_needs_rebuild(const char *out, const char *in) { #if GEBS_PLATFORM == GEBS_PLATFORM_POSIX time_t out_mtime = 0; time_t in_mtime = 0; struct stat attr1 = {0}; if (stat(out, &attr1) == 0) { out_mtime = attr1.st_mtime; } struct stat attr2 = {0}; if (stat(in, &attr2) == 0) { in_mtime = attr2.st_mtime; } return in_mtime > out_mtime; #else # error "gebs_needs_rebuild unknown platform" #endif } void gebs_rebuild_self1_alloc(Gebs_Allocator *alloc, int argc, char ** argv, int cmd_argc, char **cmd_argv, char *source) { const char *binary_path = argv[0]; if (!gebs_needs_rebuild_many(binary_path, source, __FILE__)) { return; } char old_binary_path[FILENAME_MAX]; snprintf(old_binary_path, sizeof(old_binary_path), "%s-1", binary_path); gebs_rename(binary_path, old_binary_path); Gebs_Cmd cmd = {0}; defer { gebs_cmd_free_alloc(alloc, &cmd); } for (size_t i = 0; i < cmd_argc; i++) { gebs_cmd_append_alloc(alloc, &cmd, cmd_argv[i]); } int code = gebs_cmd_run_alloc(alloc, &cmd); if (code != EXIT_SUCCESS) { gebs_rename(old_binary_path, binary_path); exit(EXIT_FAILURE); } gebs_remove(old_binary_path); cmd.count = 0; gebs_cmd_append_alloc(alloc, &cmd, (char *)binary_path); for (size_t i = 1; i < argc; i++) { gebs_cmd_append_alloc(alloc, &cmd, argv[i]); } code = gebs_cmd_run_alloc(alloc, &cmd); if (code != EXIT_SUCCESS) { exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } #endif // GEBS_IMPLEMENTATION // ---------------------------------------------------------------------------- // Prefixes // ---------------------------------------------------------------------------- #ifdef GEBS_NO_PREFIX #define SHIFT_CHECK GEBS_SHIFT_CHECK #define SHIFT GEBS_SHIFT #define LOGI GEBS_LOGI #define LOGE GEBS_LOGE #define LOGD GEBS_LOGD #define LOGW GEBS_LOGW #define LOGO GEBS_LOGO #define Allocator Gebs_Allocator #define default_allocator gebs_default_allocator #define Arena Gebs_Arena #define arena_reset gebs_arena_reset #define arena_destroy gebs_arena_destroy #define arena_make gebs_arena_make #define arena_expand gebs_arena_expand #define list_append_alloc gebs_list_append_alloc #define list_append gebs_list_append #define list_free_alloc gebs_list_free_alloc #define list_free gebs_list_free #define NString_List Gebs_NString_List #define nsl_join_alloc gebs_nls_join_alloc #define String_Builder Gebs_String_Builder #define sb_append_char_alloc gebs_sb_append_char_alloc #define sb_free_alloc gebs_sb_free_alloc #define sb_finish_alloc gebs_sb_finish_alloc #define sb_append_nstr_alloc gebs_sb_append_nstr_alloc #define sb_append_char gebs_sb_append_char #define sb_free gebs_sb_free #define sb_finish gebs_sb_finish #define sb_append_nstr gebs_sb_append_nstr #define rename1 gebs_rename #define remove1 gebs_remove #define mkdir1 gebs_mkdir #define exists1 gebs_exists #define Cmd Gebs_Cmd #define cmd_append_alloc gebs_cmd_append_alloc #define cmd_free_aloc gebs_cmd_free_alloc #define cmd_append gebs_cmd_append #define cmd_free gebs_cmd_free #define cmd_run_alloc gebs_cmd_run_alloc #define cmd_run gebs_cmd_run #define cmd_run_sync_alloc gebs_cmd_run_sync_alloc #define CMD GEBS_CMD #define cmd_run_sync_collect_alloc gebs_cmd_run_sync_collect_alloc #define cmd_run_collect_alloc gebs_cmd_run_collect_alloc #define cmd_run_collect gebs_cmd_run_collect #define needs_rebuild gebs_needs_rebuild #define needs_rebuild_many gebs_needs_rebuild_many #define rebuild_self gebs_rebuild_self #define make_compile_flags gebs_make_compile_flags #define RULE GEBS_RULE #define scratch_arena gebs_scratch_arena #define fmt gebs_fmt #define scratch_arena_reset gebs_scratch_arena_reset #endif #endif // GEBS_H_