Files
gebs/gebs.h

393 lines
12 KiB
C

#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 <stdbool.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#if GEBS_PLATFORM == GEBS_PLATFORM_POSIX
# include <unistd.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/wait.h>
#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);
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); \
})
// ----------------------------------------------------------------------------
// 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)
{
Gebs_String_Builder sb = {0};
gebs_nsl_join_alloc(alloc, cmd, &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
}
// ----------------------------------------------------------------------------
// 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_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(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_