From 6cbfd5ca91a77c8c96d6f6bcff80ef7c164e001b Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Mon, 19 May 2025 00:48:18 +0200 Subject: [PATCH] Self rebuilding --- example/.gitignore | 2 + example/gebs.c | 18 +++ example/main.c | 7 + gebs.h | 356 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 example/.gitignore create mode 100644 example/gebs.c create mode 100644 example/main.c create mode 100644 gebs.h diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..d174a82 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,2 @@ +gebs +a.out diff --git a/example/gebs.c b/example/gebs.c new file mode 100644 index 0000000..963ad35 --- /dev/null +++ b/example/gebs.c @@ -0,0 +1,18 @@ +#define GEBS_IMPLEMENTATION +#include "../gebs.h" + +int main(int argc, char ** argv) +{ + gebs_rebuild_self(argc, argv, "cc", "-o", "gebs", __FILE__); + + Gebs_Cmd cmd = {0}; + gebs_cmd_append(&cmd, "cc"); + gebs_cmd_append(&cmd, "main.c"); + + int ec = gebs_cmd_run(&cmd); + printf("%d\n", ec); + + gebs_cmd_free(&cmd); + + return 0; +} diff --git a/example/main.c b/example/main.c new file mode 100644 index 0000000..8cefa26 --- /dev/null +++ b/example/main.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + printf("Hello world\n"); + return 0; +} diff --git a/gebs.h b/gebs.h new file mode 100644 index 0000000..ce62f7a --- /dev/null +++ b/gebs.h @@ -0,0 +1,356 @@ +#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 + +#if GEBS_PLATFORM == GEBS_PLATFORM_POSIX +# include +# include +# include +# include +#else +# error "Unknown GEBS_PLATFORM" +#endif + +// ---------------------------------------------------------------------------- +// 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)(size_t size); + void (*free)(void *memory); + void *(*realloc)(void *memory, size_t prev_size, size_t new_size); +} Gebs_Allocator; + +// Wrapper macros +#define gebs_malloc(alloc, size) ((alloc)->malloc((size))) +#define gebs_free(alloc, memory) ((alloc)->free((memory))) +#define gebs_realloc(alloc, memory, prev_size, new_size) \ + ((alloc)->realloc((memory), (prev_size), (new_size))) + +extern Gebs_Allocator gebs_default_allocator; + +void *gebs_default_allocator_malloc(size_t size); +void gebs_default_allocator_free(void *memory); +void *gebs_default_allocator_realloc(void *memory, size_t prev_size, size_t new_size); + +// ---------------------------------------------------------------------------- +// 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); + +// ---------------------------------------------------------------------------- +// 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); + +// ---------------------------------------------------------------------------- +// 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) + +#ifdef GEBS_IMPLEMENTATION + +// ---------------------------------------------------------------------------- +// Commands +// ---------------------------------------------------------------------------- + +int gebs_cmd_run_sync_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd) +{ +#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 +} + +// ---------------------------------------------------------------------------- +// 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(size_t size) +{ + return malloc(size); +} + +void gebs_default_allocator_free(void *memory) +{ + free(memory); +} + +void *gebs_default_allocator_realloc(void *memory, size_t prev_size, size_t new_size) +{ + void *p = realloc(memory, new_size); + return p; +} + +// ---------------------------------------------------------------------------- +// Filesystem operations +// ---------------------------------------------------------------------------- + +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(binary_path, source)) { + 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); + code = gebs_cmd_run_alloc(alloc, &cmd); + if (code != EXIT_SUCCESS) { + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); +} + +#endif // GEBS_IMPLEMENTATION + +#endif // GEBS_H_