--- /dev/null
+#ifndef CBUILD_H
+#define CBUILD_H
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
+# define COMPILER_GCC 1
+#elif defined(__clang__) && !defined(_MSC_VER)
+# define COMPILER_CLANG 1
+#elif defined(_MSC_VER)
+# define COMPILER_CL 1
+#else
+# error Unsupported compiler
+#endif
+
+#if defined(__gnu_linux__)
+# define OS_LINUX 1
+#elif defined(__unix__)
+# define OS_BSD 1
+#elif defined(_WIN32)
+# define OS_WINDOWS 1
+#elif defined(__APPLE__)
+# define OS_MAC 1
+#else
+# error Unknown operating system
+#endif
+
+#if defined(__cplusplus)
+# define CPP 1
+#else
+# define CPP 0
+#endif
+
+#if !defined(COMPILER_GCC)
+# define COMPILER_GCC 0
+#endif
+#if !defined(COMPILER_CLANG)
+# define COMPILER_CLANG 0
+#endif
+#if !defined(COMPILER_CL)
+# define COMPILER_CL 0
+#endif
+
+#if !defined(OS_LINUX)
+# define OS_LINUX 0
+#endif
+#if !defined(OS_BSD)
+# define OS_BSD 0
+#endif
+#if !defined(OS_MAC)
+# define OS_MAC 0
+#endif
+#if !defined(OS_WINDOWS)
+# define OS_WINDOWS 0
+#endif
+#if !defined(OS_NONE)
+# define OS_NONE 0
+#endif
+
+#if !CPP
+# if __STDC_VERSION__ >= 199901L
+# include <stdbool.h>
+# else
+typedef enum {false, true} bool;
+# endif
+#endif
+
+#ifndef _assert_break
+# if OS_WINDOWS
+# define _assert_break() __debugbreak()
+# else
+# define _assert_break() __builtin_trap()
+# endif
+#endif
+#define Assert(COND) do { if (!(COND)) { _assert_break(); } } while (0)
+#define StackPush(Head, Nodeptr) \
+ LLPushFrontCustom((Head), (Head), (Nodeptr), next)
+#define StackPop(Head) (Head ? (Head = Head->next) : 0)
+#define LLPushFrontCustom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr) \
+ : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
+#define QueuePush(Head, Last, Nodeptr) \
+ LLPushBackCustom((Head), (Last), (Nodeptr), next)
+#define QueuePop(Head) (Head ? (Head = Head->next) : 0)
+#define LLPushBackCustom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Last) = (Nodeptr) \
+ : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr)) \
+ : ((Head)->Next = (Last) = (Nodeptr))))
+
+struct cb_path_list {
+ char **values;
+ size_t count;
+ size_t capacity;
+};
+
+typedef struct cb_path_list cb_cmd;
+
+struct cb_run_args {
+ bool async;
+ bool reset;
+};
+
+#ifndef CB_DYN_DEFAULT_CAPACITY
+# define CB_DYN_DEFAULT_CAPACITY 8
+#endif
+
+#if OS_WINDOWS
+# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) "cl.exe", "/Fe:" (Exe_name), (Builder_src)
+#else
+# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) "cc", "-o", (Exe_name), (Builder_src)
+#endif
+
+#define cb_dyn_reserve(Dynarr, HowMany) cb_dyn_reserve_custom((Dynarr), (HowMany), values, count, capacity)
+#define cb_dyn_free(Dynarr) cb_dyn_free_custom((Dynarr), values)
+#define cb_dyn_push(Dynarr, Node) cb_dyn_push_custom((Dynarr), (Node), values, count, capacity)
+#define cb_dyn_append(Dynarr, Array, Size) cb_dyn_append_custom((Dynarr), (Array), (Size), values, count, capacity)
+#define cb_cmd_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
+#define cb_cmd_append_dyn(Dynarr, Values, Count) cb_dyn_append((Dynarr), (Values), (Count));
+#define cb_cmd_append(Dynarr, ...) cb_dyn_append((Dynarr), \
+ ((char*[]){__VA_ARGS__}), \
+ sizeof((char*[]){__VA_ARGS__})/sizeof(char*))
+#define cb_rebuild(argc, argv) _cb_rebuild(argc, argv, __FILE__, 0)
+#define cb_rebuild_with(argc, argv, ...) _cb_rebuild(argc, argv, __FILE__, __VA_ARGS__, 0)
+#define cb_run(Cmd, ...) _cb_run((Cmd), (struct cb_run_args) { \
+ .async = false, \
+ .reset = true, \
+ __VA_ARGS__ \
+ })
+
+#define cb_dyn_free_custom(Dynarr, Values) free((Dynarr)->Values)
+#define cb_dyn_reserve_custom(Dynarr, HowMany, Values, Count, Capacity) \
+ do { \
+ if (!(Dynarr)->Capacity) { \
+ (Dynarr)->Capacity = CB_DYN_DEFAULT_CAPACITY; \
+ } \
+ while ((HowMany) > (Dynarr)->Capacity) { \
+ (Dynarr)->Capacity *= 2; \
+ } \
+ (Dynarr)->Values = realloc((Dynarr)->Values, (Dynarr)->Capacity * \
+ sizeof((Dynarr)->Values[0])); \
+ Assert((Dynarr)->Values); \
+ } while(0)
+#define cb_dyn_push_custom(Dynarr, Node, Values, Count, Capacity) \
+ do { \
+ cb_dyn_reserve_custom((Dynarr), (Dynarr)->Count + 1, \
+ Values, Count, Capacity); \
+ (Dynarr)->Values[(Dynarr)->Count++] = (Node); \
+ } while(0)
+#define cb_dyn_append_custom(Dynarr, Array, Size, Values, Count, Capacity) \
+ do { \
+ cb_dyn_reserve((Dynarr), (Size)); \
+ memcpy((Dynarr)->Values + (Dynarr)->Count, (Array), \
+ (Size) * sizeof((Dynarr)->Values[0])); \
+ (Dynarr)->Count += (Size); \
+ } while(0)
+
+static bool _cb_need_rebuild(char *output_path, struct cb_path_list sources);
+static void _cb_rebuild(int argc, char **argv, char *cb_src, ...);
+static void _cb_run(cb_cmd *cmd, struct cb_run_args args);
+
+static void _cb_rebuild(int argc, char **argv, char *builder_src, ...) {
+ Assert(argc >= 1);
+ char *exe_name = argv[0];
+
+ struct cb_path_list sources = {};
+ cb_dyn_push(&sources, builder_src);
+
+ va_list args;
+ va_start(args, builder_src);
+ for (;;) {
+ char *path = va_arg(args, char*);
+ if (!path) { break; }
+ cb_dyn_push(&sources, path);
+ }
+ va_end(args);
+
+ if (!_cb_need_rebuild(exe_name, sources)) {
+ cb_dyn_free(&sources);
+ return;
+ }
+
+ cb_cmd cmd = {};
+ cb_cmd_append(&cmd, CB_CMD_REBUILD_SELF(exe_name, builder_src));
+ cb_run(&cmd);
+
+ cb_cmd_push(&cmd, exe_name);
+ cb_cmd_append_dyn(&cmd, argv, argc);
+ cb_run(&cmd);
+ exit(0);
+}
+
+static bool _cb_need_rebuild(char *output_path, struct cb_path_list sources) {
+ // NOTE(lb): on `fstat` failure assume needed rebuild
+ struct stat output_stat = {};
+ if (stat(output_path, &output_stat) < 0) { return true; }
+ for (size_t i = 0; i < sources.count; ++i) {
+ struct stat source_stat = {};
+ if (stat(sources.values[i], &source_stat) < 0 ||
+ output_stat.st_mtime < source_stat.st_mtime) { return true; }
+ }
+ return false;
+}
+
+static void _cb_run(cb_cmd *cmd, struct cb_run_args args) {
+ pid_t child_pid = fork();
+ if (child_pid) {
+ if (args.reset) {
+ cmd->count = 0;
+ }
+ if (!args.async) {
+ Assert(waitpid(child_pid, 0, 0) == child_pid);
+ }
+ } else {
+ cb_cmd _cmd = {};
+ cb_cmd_append_dyn(&_cmd, cmd->values, cmd->count);
+ cb_cmd_push(&_cmd, 0);
+ exit(execvp(cmd->values[0], cmd->values));
+ }
+}
+
+#endif