diff options
| -rw-r--r-- | b.c | 52 | ||||
| l---------[-rw-r--r--] | src/b.h | 904 |
2 files changed, 27 insertions, 929 deletions
@@ -5,12 +5,12 @@ #define BUILD_DIR "build/" #define TEMPLATE_DIR BUILD_DIR"templates/" -int debug_level = 3; +int debug_level = 0; bool force = false; bool tests = false; void -debug_or_release(B_Cmd* cmd) +debug_or_release(b_cmd* cmd) { if (debug_level == 0) b_cmd_append(cmd, "-O3", "-s", "-DNDEBUG"); @@ -27,13 +27,13 @@ debug_or_release(B_Cmd* cmd) } void -inlcude_dirs(B_Cmd* cmd) +inlcude_dirs(b_cmd* cmd) { b_cmd_append(cmd, "-I./src/", "-I./"TEMPLATE_DIR); } void -cflags_common(B_Cmd* cmd) +cflags_common(b_cmd* cmd) { b_cmd_append(cmd, "-Wall", "-Wextra"); @@ -44,7 +44,7 @@ cflags_common(B_Cmd* cmd) } void -synth_libs(B_Cmd *cmd) +synth_libs(b_cmd *cmd) { b_cmd_append(cmd, "-lportaudio", "-lrt", "-lasound", "-lraylib", "-lportmidi", "-lfftw3f", "-lsndfile", "-lconfig", "-lpthread", @@ -54,21 +54,21 @@ synth_libs(B_Cmd *cmd) } void -sequencer_libs(B_Cmd *cmd) +sequencer_libs(b_cmd *cmd) { b_cmd_append(cmd, "-lportaudio", "-lportmidi", "-lrt", "-lraylib", "-lpthread"); inlcude_dirs(cmd); } -void cc(B_Cmd *cmd) +void cc(b_cmd *cmd) { b_cmd_append(cmd, "clang"); cflags_common(cmd); } bool -build_c(B_Cmd* cmd, +build_c(b_cmd* cmd, const char** input_paths, size_t input_paths_len, const char** dep_paths, @@ -76,12 +76,12 @@ build_c(B_Cmd* cmd, const char* output_path) { int rebuild_is_needed = - b_needs_rebuild(B_COMPILE, output_path, input_paths, input_paths_len); + b_needs_rebuild(output_path, input_paths, input_paths_len); int dep_rebuild = 0; if (rebuild_is_needed == 0) dep_rebuild = - b_needs_rebuild(B_COMPILE, output_path, dep_paths, dep_paths_len); + b_needs_rebuild(output_path, dep_paths, dep_paths_len); if (rebuild_is_needed < 0 || dep_rebuild < 0) return false; @@ -90,19 +90,19 @@ build_c(B_Cmd* cmd, cmd->count = 0; cc(cmd); b_cmd_append(cmd, "-o", output_path); - b_da_append_many(cmd, input_paths, input_paths_len); + array_append_many(cmd, input_paths, input_paths_len); if (0 == strcmp(output_path, BUILD_DIR"synth")) synth_libs(cmd); if (0 == strcmp(output_path, BUILD_DIR"sequencer")) sequencer_libs(cmd); - return b_cmd_run_sync(*cmd); + return b_cmd_run(cmd); } b_log(B_INFO, "%s is up-to-date", output_path); return true; } -bool build_templates(B_Cmd *cmd, const char **templates, size_t len) { +bool build_templates(b_cmd *cmd, const char **templates, size_t len) { char dest[1024] = ""; for (size_t i = 0; i < len; i++) { cmd->count = 0; @@ -110,7 +110,7 @@ bool build_templates(B_Cmd *cmd, const char **templates, size_t len) { strcpy(dest, TEMPLATE_DIR); strcat(dest, basename(base)); strcat(dest, ".h"); - if (b_needs_rebuild1(B_TEMPLATE, dest, templates[i]) == 0 && + if (b_needs_rebuild1(dest, templates[i]) == 0 && force == false) { b_log(B_INFO, "%s is up-to-date", dest); continue; @@ -121,15 +121,15 @@ bool build_templates(B_Cmd *cmd, const char **templates, size_t len) { strcat(tmp, " > "); strcat(tmp, dest); b_cmd_append(cmd, "bash", "-c", tmp); - b_cmd_run_sync(*cmd); + b_cmd_run(cmd); } return true; } bool -build_tests(B_Cmd *cmd, const char * output_path) { +build_tests(b_cmd *cmd, const char * output_path) { int rebuild_is_needed = - b_needs_rebuild1(B_COMPILE, output_path, "tests/example_test.c"); + b_needs_rebuild1(output_path, "tests/example_test.c"); /* int dep_rebuild = 0; */ /* if (rebuild_is_needed == 0) */ @@ -147,7 +147,7 @@ build_tests(B_Cmd *cmd, const char * output_path) { b_cmd_append(cmd, "-l", "check"); b_cmd_append(cmd, "tests/example_test.c"); synth_libs(cmd); - return b_cmd_run_sync(*cmd); + return b_cmd_run(cmd); } b_log(B_INFO, "%s is up-to-date", output_path); @@ -240,24 +240,24 @@ main(int argc, char *argv[]) "tmpl/index.html" }; - B_Cmd cmd = {0}; + b_cmd cmd = {0}; - b_mkdir_if_not_exists(BUILD_DIR); - b_mkdir_if_not_exists(TEMPLATE_DIR); + b_mkdir(BUILD_DIR); + b_mkdir(TEMPLATE_DIR); if (!build_c(&cmd, (const char *[]){"src/tt.c"}, 1, NULL, 0, BUILD_DIR "tt")) return 1; - if (!build_templates(&cmd, templates, B_ARRAY_LEN(templates))) + if (!build_templates(&cmd, templates, ARRAY_LENGTH(templates))) return 1; - if (!build_c(&cmd, synth_paths, B_ARRAY_LEN(synth_paths), synth_deps, - B_ARRAY_LEN(synth_deps), BUILD_DIR "synth")) + if (!build_c(&cmd, synth_paths, ARRAY_LENGTH(synth_paths), synth_deps, + ARRAY_LENGTH(synth_deps), BUILD_DIR "synth")) return 1; - if (!build_c(&cmd, sequencer_paths, B_ARRAY_LEN(sequencer_paths), sequencer_deps, - B_ARRAY_LEN(sequencer_deps), BUILD_DIR "sequencer")) + if (!build_c(&cmd, sequencer_paths, ARRAY_LENGTH(sequencer_paths), sequencer_deps, + ARRAY_LENGTH(sequencer_deps), BUILD_DIR "sequencer")) return 1; if (tests && !build_tests(&cmd, BUILD_DIR "test")) @@ -1,903 +1 @@ -// This is an attempt on build library for building C with C akin to nob.h from -// tsoding which is itself based on https://github.com/tsoding/nobuild -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef B_H_ -#define B_H_ - -#define B_ASSERT assert -#define B_REALLOC realloc -#define B_FREE free - -#include <assert.h> -#include <stdbool.h> -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <errno.h> -#include <ctype.h> - -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <dirent.h> - -#define B_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) -#define B_ARRAY_GET(array, index) \ - (B_ASSERT(index >= 0), B_ASSERT(index < B_ARRAY_LEN(array)), array[index]) - -typedef enum { - B_INFO, - B_CMD, - B_COMPILE, - B_TEMPLATE, - B_CHANGE, - B_WARNING, - B_ERROR, -} B_Log_Level; - -void b_log(B_Log_Level level, const char *fmt, ...); - -// It is an equivalent of shift command from bash. It basically pops a command line -// argument from the beginning. -char *b_shift_args(int *argc, char ***argv); - -typedef struct { - const char **items; - size_t count; - size_t capacity; -} B_File_Paths; - -typedef enum { - B_FILE_REGULAR = 0, - B_FILE_DIRECTORY, - B_FILE_SYMLINK, - B_FILE_OTHER, -} B_File_Type; - -bool b_mkdir_if_not_exists(const char *path); -bool b_copy_file(const char *src_path, const char *dst_path); -bool b_copy_directory_recursively(const char *src_path, const char *dst_path); -bool b_read_entire_dir(const char *parent, B_File_Paths *children); -bool b_write_entire_file(const char *path, const void *data, size_t size); -B_File_Type b_get_file_type(const char *path); - -#define b_return_defer(value) do { result = (value); goto defer; } while(0) - -// Initial capacity of a dynamic array -#define B_DA_INIT_CAP 256 - -// Append an item to a dynamic array -#define b_da_append(da, item) \ - do { \ - if ((da)->count >= (da)->capacity) { \ - (da)->capacity = (da)->capacity == 0 ? B_DA_INIT_CAP : (da)->capacity*2; \ - (da)->items = B_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ - B_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ - } \ - \ - (da)->items[(da)->count++] = (item); \ - } while (0) - -#define b_da_free(da) B_FREE((da).items) - -// Append several items to a dynamic array -#define b_da_append_many(da, new_items, new_items_count) \ - do { \ - if ((da)->count + (new_items_count) > (da)->capacity) { \ - if ((da)->capacity == 0) { \ - (da)->capacity = B_DA_INIT_CAP; \ - } \ - while ((da)->count + (new_items_count) > (da)->capacity) { \ - (da)->capacity *= 2; \ - } \ - (da)->items = B_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ - B_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ - } \ - memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ - (da)->count += (new_items_count); \ - } while (0) - -typedef struct { - char *items; - size_t count; - size_t capacity; -} B_String_Builder; - -bool b_read_entire_file(const char *path, B_String_Builder *sb); - -// Append a sized buffer to a string builder -#define b_sb_append_buf(sb, buf, size) b_da_append_many(sb, buf, size) - -// Append a NULL-terminated string to a string builder -#define b_sb_append_cstr(sb, cstr) \ - do { \ - const char *s = (cstr); \ - size_t n = strlen(s); \ - b_da_append_many(sb, s, n); \ - } while (0) - -// Append a single NULL character at the end of a string builder. So then you can -// use it a NULL-terminated C string -#define b_sb_append_null(sb) b_da_append_many(sb, "", 1) - -// Free the memory allocated by a string builder -#define b_sb_free(sb) B_FREE((sb).items) - -// Process handle -typedef int B_Proc; -#define B_INVALID_PROC (-1) - -typedef struct { - B_Proc *items; - size_t count; - size_t capacity; -} B_Procs; - -bool b_procs_wait(B_Procs procs); - -// Wait until the process has finished -bool b_proc_wait(B_Proc proc); - -// A command - the main workhorse of B. B is all about building commands an running them -typedef struct { - const char **items; - size_t count; - size_t capacity; -} B_Cmd; - -// Render a string representation of a command into a string builder. Keep in mind the the -// string builder is not NULL-terminated by default. Use b_sb_append_null if you plan to -// use it as a C string. -void b_cmd_render(B_Cmd cmd, B_String_Builder *render); - -#define b_cmd_append(cmd, ...) \ - b_da_append_many(cmd, ((const char*[]){__VA_ARGS__}), (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))) - -// Free all the memory allocated by command arguments -#define b_cmd_free(cmd) B_FREE(cmd.items) - -// Run command asynchronously -B_Proc b_cmd_run_async(B_Cmd cmd); - -// Run command synchronously -bool b_cmd_run_sync(B_Cmd cmd); - -#ifndef B_TEMP_CAPACITY -#define B_TEMP_CAPACITY (8*1024*1024) -#endif // B_TEMP_CAPACITY -char *b_temp_strdup(const char *cstr); -void *b_temp_alloc(size_t size); -char *b_temp_sprintf(const char *format, ...); -void b_temp_reset(void); -size_t b_temp_save(void); -void b_temp_rewind(size_t checkpoint); - -int is_path1_modified_after_path2(const char *path1, const char *path2); -bool b_rename(const char *old_path, const char *new_path); -int b_needs_rebuild(B_Log_Level log, const char *output_path, const char **input_paths, size_t input_paths_count); -int b_needs_rebuild1(B_Log_Level log, const char *output_path, const char *input_path); -int b_file_exists(const char *file_path); - -// TODO: add MinGW support for Go Rebuild Urself™ Technology -#ifndef B_REBUILD_URSELF -# if _WIN32 -# if defined(__GNUC__) -# define B_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path -# elif defined(__clang__) -# define B_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path -# elif defined(_MSC_VER) -# define B_REBUILD_URSELF(binary_path, source_path) "cl.exe", b_temp_sprintf("/Fe:%s", (binary_path)), source_path -# endif -# else -# if defined(__clang__) -# define B_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path -# else -# define B_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path -# endif -# endif -#endif - -// Go Rebuild Urself™ Technology -// -// How to use it: -// int main(int argc, char** argv) { -// GO_REBUILD_URSELF(argc, argv); -// // actual work -// return 0; -// } -// -// After your added this macro every time you run ./build it will detect -// that you modified its original source code and will try to rebuild itself -// before doing any actual work. So you only need to bootstrap your build system -// once. -// -// The modification is detected by comparing the last modified times of the executable -// and its source code. The same way the make utility usually does it. -// -// The rebuilding is done by using the REBUILD_URSELF macro which you can redefine -// if you need a special way of bootstraping your build system. (which I personally -// do not recommend since the whole idea of build is to keep the process of bootstrapping -// as simple as possible and doing all of the actual work inside of the build) -// -#define B_GO_REBUILD_URSELF(argc, argv) \ - do { \ - const char *source_path = __FILE__; \ - assert(argc >= 1); \ - const char *binary_path = argv[0]; \ - \ - int rebuild_is_needed = b_needs_rebuild(B_COMPILE, binary_path, &source_path, 1); \ - if (rebuild_is_needed < 0) exit(1); \ - if (rebuild_is_needed) { \ - B_String_Builder sb = {0}; \ - b_sb_append_cstr(&sb, binary_path); \ - b_sb_append_cstr(&sb, ".old"); \ - b_sb_append_null(&sb); \ - \ - if (!b_rename(binary_path, sb.items)) exit(1); \ - B_Cmd rebuild = {0}; \ - b_cmd_append(&rebuild, B_REBUILD_URSELF(binary_path, source_path)); \ - bool rebuild_succeeded = b_cmd_run_sync(rebuild); \ - b_cmd_free(rebuild); \ - if (!rebuild_succeeded) { \ - b_rename(sb.items, binary_path); \ - exit(1); \ - } \ - \ - B_Cmd cmd = {0}; \ - b_da_append_many(&cmd, argv, argc); \ - if (!b_cmd_run_sync(cmd)) exit(1); \ - exit(0); \ - } \ - } while(0) -// The implementation idea is stolen from https://github.com/zhiayang/nabs - -typedef struct { - size_t count; - const char *data; -} B_String_View; - -const char *b_temp_sv_to_cstr(B_String_View sv); - -B_String_View b_sv_chop_by_delim(B_String_View *sv, char delim); -B_String_View b_sv_trim(B_String_View sv); -bool b_sv_eq(B_String_View a, B_String_View b); -B_String_View b_sv_from_cstr(const char *cstr); -B_String_View b_sv_from_parts(const char *data, size_t count); - -// printf macros for String_View -#ifndef SV_Fmt -#define SV_Fmt "%.*s" -#endif // SV_Fmt -#ifndef SV_Arg -#define SV_Arg(sv) (int) (sv).count, (sv).data -#endif // SV_Arg -// USAGE: -// String_View name = ...; -// printf("Name: "SV_Fmt"\n", SV_Arg(name)); - -/* file.c */ -/* file.h + */ -/* -------- */ -/* file.o */ - -/* temlp.h */ -/* f1.c */ -/* f2.c */ -/* f3.c + */ -/* ------ */ -/* templ.o */ - -/* prog.c */ -/* obj1.o */ -/* obj2.o + */ -/* -------- */ -/* prog */ - - - -#endif // B_H_ - -#ifdef B_IMPLEMENTATION - -static size_t b_temp_size = 0; -static char b_temp[B_TEMP_CAPACITY] = {0}; - -bool b_mkdir_if_not_exists(const char *path) -{ - int result = mkdir(path, 0755); - if (result < 0) { - if (errno == EEXIST) { - //b_log(B_INFO, "directory `%s` already exists", path); - return true; - } - b_log(B_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); - return false; - } - - b_log(B_INFO, "created directory `%s`", path); - return true; -} - -bool b_copy_file(const char *src_path, const char *dst_path) -{ - b_log(B_INFO, "copying %s -> %s", src_path, dst_path); - - int src_fd = -1; - int dst_fd = -1; - size_t buf_size = 32*1024; - char *buf = B_REALLOC(NULL, buf_size); - B_ASSERT(buf != NULL && "Buy more RAM lol!!"); - bool result = true; - - src_fd = open(src_path, O_RDONLY); - if (src_fd < 0) { - b_log(B_ERROR, "Could not open file %s: %s", src_path, strerror(errno)); - b_return_defer(false); - } - - struct stat src_stat; - if (fstat(src_fd, &src_stat) < 0) { - b_log(B_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno)); - b_return_defer(false); - } - - dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode); - if (dst_fd < 0) { - b_log(B_ERROR, "Could not create file %s: %s", dst_path, strerror(errno)); - b_return_defer(false); - } - - for (;;) { - ssize_t n = read(src_fd, buf, buf_size); - if (n == 0) break; - if (n < 0) { - b_log(B_ERROR, "Could not read from file %s: %s", src_path, strerror(errno)); - b_return_defer(false); - } - char *buf2 = buf; - while (n > 0) { - ssize_t m = write(dst_fd, buf2, n); - if (m < 0) { - b_log(B_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno)); - b_return_defer(false); - } - n -= m; - buf2 += m; - } - } - -defer: - free(buf); - close(src_fd); - close(dst_fd); - return result; -} - -void b_cmd_render(B_Cmd cmd, B_String_Builder *render) -{ - for (size_t i = 0; i < cmd.count; ++i) { - const char *arg = cmd.items[i]; - if (arg == NULL) break; - if (i > 0) b_sb_append_cstr(render, " "); - if (!strchr(arg, ' ')) { - b_sb_append_cstr(render, arg); - } else { - b_da_append(render, '\''); - b_sb_append_cstr(render, arg); - b_da_append(render, '\''); - } - } -} - -B_Proc b_cmd_run_async(B_Cmd cmd) -{ - if (cmd.count < 1) { - b_log(B_ERROR, "Could not run empty command"); - return B_INVALID_PROC; - } - - B_String_Builder sb = {0}; - b_cmd_render(cmd, &sb); - b_sb_append_null(&sb); - b_log(B_CMD, "%s", sb.items); - b_sb_free(sb); - memset(&sb, 0, sizeof(sb)); - - pid_t cpid = fork(); - if (cpid < 0) { - b_log(B_ERROR, "Could not fork child process: %s", strerror(errno)); - return B_INVALID_PROC; - } - - if (cpid == 0) { - // NOTE: This leaks a bit of memory in the child process. - // But do we actually care? It's a one off leak anyway... - B_Cmd cmd_null = {0}; - b_da_append_many(&cmd_null, cmd.items, cmd.count); - b_cmd_append(&cmd_null, NULL); - - if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { - b_log(B_ERROR, "Could not exec child process: %s", strerror(errno)); - exit(1); - } - B_ASSERT(0 && "unreachable"); - } - - return cpid; -} - -bool b_procs_wait(B_Procs procs) -{ - bool success = true; - for (size_t i = 0; i < procs.count; ++i) { - success = b_proc_wait(procs.items[i]) && success; - } - return success; -} - -bool b_proc_wait(B_Proc proc) -{ - if (proc == B_INVALID_PROC) return false; - - for (;;) { - int wstatus = 0; - if (waitpid(proc, &wstatus, 0) < 0) { - b_log(B_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno)); - return false; - } - - if (WIFEXITED(wstatus)) { - int exit_status = WEXITSTATUS(wstatus); - if (exit_status != 0) { - b_log(B_ERROR, "command exited with exit code %d", exit_status); - return false; - } - - break; - } - - if (WIFSIGNALED(wstatus)) { - b_log(B_ERROR, "command process was terminated by %s", strsignal(WTERMSIG(wstatus))); - return false; - } - } - - return true; -} - -bool b_cmd_run_sync(B_Cmd cmd) -{ - B_Proc p = b_cmd_run_async(cmd); - if (p == B_INVALID_PROC) return false; - return b_proc_wait(p); -} - -char *b_shift_args(int *argc, char ***argv) -{ - B_ASSERT(*argc > 0); - char *result = **argv; - (*argv) += 1; - (*argc) -= 1; - return result; -} - -void b_log(B_Log_Level level, const char *fmt, ...) -{ - switch (level) { - case B_INFO: - fprintf(stderr, " [INFO] "); - break; - case B_CMD: - fprintf(stderr, " [CMD] "); - break; - case B_COMPILE: - fprintf(stderr, " [COMPILE] "); - break; - case B_TEMPLATE: - fprintf(stderr, "[TEMPLATE] "); - break; - case B_CHANGE: - fprintf(stderr, " [CHANGE] "); - break; - case B_WARNING: - fprintf(stderr, " [WARNING] "); - break; - case B_ERROR: - fprintf(stderr, " [ERROR] "); - break; - default: - B_ASSERT(0 && "unreachable"); - } - - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - fprintf(stderr, "\n"); -} - -bool b_read_entire_dir(const char *parent, B_File_Paths *children) -{ - bool result = true; - DIR *dir = NULL; - - dir = opendir(parent); - if (dir == NULL) { - b_log(B_ERROR, "Could not open directory %s: %s", parent, strerror(errno)); - b_return_defer(false); - } - - errno = 0; - struct dirent *ent = readdir(dir); - while (ent != NULL) { - b_da_append(children, b_temp_strdup(ent->d_name)); - ent = readdir(dir); - } - - if (errno != 0) { - b_log(B_ERROR, "Could not read directory %s: %s", parent, strerror(errno)); - b_return_defer(false); - } - -defer: - if (dir) closedir(dir); - return result; -} - -bool b_write_entire_file(const char *path, const void *data, size_t size) -{ - bool result = true; - - FILE *f = fopen(path, "wb"); - if (f == NULL) { - b_log(B_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); - b_return_defer(false); - } - - // len - // v - // aaaaaaaaaa - // ^ - // data - - const char *buf = data; - while (size > 0) { - size_t n = fwrite(buf, 1, size, f); - if (ferror(f)) { - b_log(B_ERROR, "Could not write into file %s: %s\n", path, strerror(errno)); - b_return_defer(false); - } - size -= n; - buf += n; - } - -defer: - if (f) fclose(f); - return result; -} - -B_File_Type b_get_file_type(const char *path) -{ - struct stat statbuf; - if (stat(path, &statbuf) < 0) { - b_log(B_ERROR, "Could not get stat of %s: %s", path, strerror(errno)); - return -1; - } - - switch (statbuf.st_mode & S_IFMT) { - case S_IFDIR: return B_FILE_DIRECTORY; - case S_IFREG: return B_FILE_REGULAR; - case S_IFLNK: return B_FILE_SYMLINK; - default: return B_FILE_OTHER; - } -} - -bool b_copy_directory_recursively(const char *src_path, const char *dst_path) -{ - bool result = true; - B_File_Paths children = {0}; - B_String_Builder src_sb = {0}; - B_String_Builder dst_sb = {0}; - size_t temp_checkpoint = b_temp_save(); - - B_File_Type type = b_get_file_type(src_path); - if (type < 0) return false; - - switch (type) { - case B_FILE_DIRECTORY: { - if (!b_mkdir_if_not_exists(dst_path)) b_return_defer(false); - if (!b_read_entire_dir(src_path, &children)) b_return_defer(false); - - for (size_t i = 0; i < children.count; ++i) { - if (strcmp(children.items[i], ".") == 0) continue; - if (strcmp(children.items[i], "..") == 0) continue; - - src_sb.count = 0; - b_sb_append_cstr(&src_sb, src_path); - b_sb_append_cstr(&src_sb, "/"); - b_sb_append_cstr(&src_sb, children.items[i]); - b_sb_append_null(&src_sb); - - dst_sb.count = 0; - b_sb_append_cstr(&dst_sb, dst_path); - b_sb_append_cstr(&dst_sb, "/"); - b_sb_append_cstr(&dst_sb, children.items[i]); - b_sb_append_null(&dst_sb); - - if (!b_copy_directory_recursively(src_sb.items, dst_sb.items)) { - b_return_defer(false); - } - } - } break; - - case B_FILE_REGULAR: { - if (!b_copy_file(src_path, dst_path)) { - b_return_defer(false); - } - } break; - - case B_FILE_SYMLINK: { - b_log(B_WARNING, "TODO: Copying symlinks is not supported yet"); - } break; - - case B_FILE_OTHER: { - b_log(B_ERROR, "Unsupported type of file %s", src_path); - b_return_defer(false); - } break; - - default: B_ASSERT(0 && "unreachable"); - } - -defer: - b_temp_rewind(temp_checkpoint); - b_da_free(src_sb); - b_da_free(dst_sb); - b_da_free(children); - return result; -} - -char *b_temp_strdup(const char *cstr) -{ - size_t n = strlen(cstr); - char *result = b_temp_alloc(n + 1); - B_ASSERT(result != NULL && "Increase B_TEMP_CAPACITY"); - memcpy(result, cstr, n); - result[n] = '\0'; - return result; -} - -void *b_temp_alloc(size_t size) -{ - if (b_temp_size + size > B_TEMP_CAPACITY) return NULL; - void *result = &b_temp[b_temp_size]; - b_temp_size += size; - return result; -} - -char *b_temp_sprintf(const char *format, ...) -{ - va_list args; - va_start(args, format); - int n = vsnprintf(NULL, 0, format, args); - va_end(args); - - B_ASSERT(n >= 0); - char *result = b_temp_alloc(n + 1); - B_ASSERT(result != NULL && "Extend the size of the temporary allocator"); - // TODO: use proper arenas for the temporary allocator; - va_start(args, format); - vsnprintf(result, n + 1, format, args); - va_end(args); - - return result; -} - -void b_temp_reset(void) -{ - b_temp_size = 0; -} - -size_t b_temp_save(void) -{ - return b_temp_size; -} - -void b_temp_rewind(size_t checkpoint) -{ - b_temp_size = checkpoint; -} - -const char *b_temp_sv_to_cstr(B_String_View sv) -{ - char *result = b_temp_alloc(sv.count + 1); - B_ASSERT(result != NULL && "Extend the size of the temporary allocator"); - memcpy(result, sv.data, sv.count); - result[sv.count] = '\0'; - return result; -} - -int b_needs_rebuild(B_Log_Level log, const char *output_path, const char **input_paths, size_t input_paths_count) -{ - struct stat statbuf = {0}; - - if (stat(output_path, &statbuf) < 0) { - // NOTE: if output does not exist it 100% must be rebuilt - if (errno == ENOENT) { - b_log(log, "%s", output_path); - return 1; - } - b_log(B_ERROR, "could not stat %s: %s", output_path, strerror(errno)); - return -1; - } - int output_path_time = statbuf.st_mtime; - - for (size_t i = 0; i < input_paths_count; ++i) { - const char *input_path = input_paths[i]; - if (stat(input_path, &statbuf) < 0) { - // NOTE: non-existing input is an error cause it is needed for building in the first place - b_log(B_ERROR, "could not stat %s: %s", input_path, strerror(errno)); - return -1; - } - int input_path_time = statbuf.st_mtime; - // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild - if (input_path_time > output_path_time) { - b_log(B_CHANGE, "%s", input_path); - b_log(log, "%s", output_path); - return 1; - } - } - - return 0; -} - -int b_needs_rebuild1(B_Log_Level log, const char *output_path, const char *input_path) -{ - return b_needs_rebuild(log, output_path, &input_path, 1); -} - -bool b_rename(const char *old_path, const char *new_path) -{ - b_log(B_INFO, "renaming %s -> %s", old_path, new_path); - if (rename(old_path, new_path) < 0) { - b_log(B_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); - return false; - } - return true; -} - -bool b_read_entire_file(const char *path, B_String_Builder *sb) -{ - bool result = true; - - FILE *f = fopen(path, "rb"); - if (f == NULL) b_return_defer(false); - if (fseek(f, 0, SEEK_END) < 0) b_return_defer(false); - long m = ftell(f); - if (m < 0) b_return_defer(false); - if (fseek(f, 0, SEEK_SET) < 0) b_return_defer(false); - - size_t new_count = sb->count + m; - if (new_count > sb->capacity) { - sb->items = realloc(sb->items, new_count); - B_ASSERT(sb->items != NULL && "Buy more RAM lool!!"); - sb->capacity = new_count; - } - - fread(sb->items + sb->count, m, 1, f); - if (ferror(f)) { - // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case. - b_return_defer(false); - } - sb->count = new_count; - -defer: - if (!result) b_log(B_ERROR, "Could not read file %s: %s", path, strerror(errno)); - if (f) fclose(f); - return result; -} - -B_String_View b_sv_chop_by_delim(B_String_View *sv, char delim) -{ - size_t i = 0; - while (i < sv->count && sv->data[i] != delim) { - i += 1; - } - - B_String_View result = b_sv_from_parts(sv->data, i); - - if (i < sv->count) { - sv->count -= i + 1; - sv->data += i + 1; - } else { - sv->count -= i; - sv->data += i; - } - - return result; -} - -B_String_View b_sv_from_parts(const char *data, size_t count) -{ - B_String_View sv; - sv.count = count; - sv.data = data; - return sv; -} - -B_String_View b_sv_trim_left(B_String_View sv) -{ - size_t i = 0; - while (i < sv.count && isspace(sv.data[i])) { - i += 1; - } - - return b_sv_from_parts(sv.data + i, sv.count - i); -} - -B_String_View b_sv_trim_right(B_String_View sv) -{ - size_t i = 0; - while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { - i += 1; - } - - return b_sv_from_parts(sv.data, sv.count - i); -} - -B_String_View b_sv_trim(B_String_View sv) -{ - return b_sv_trim_right(b_sv_trim_left(sv)); -} - -B_String_View b_sv_from_cstr(const char *cstr) -{ - return b_sv_from_parts(cstr, strlen(cstr)); -} - -bool b_sv_eq(B_String_View a, B_String_View b) -{ - if (a.count != b.count) { - return false; - } else { - return memcmp(a.data, b.data, a.count) == 0; - } -} - -// RETURNS: -// 0 - file does not exists -// 1 - file exists -// -1 - error while checking if file exists. The error is logged -int b_file_exists(const char *file_path) -{ - struct stat statbuf; - if (stat(file_path, &statbuf) < 0) { - if (errno == ENOENT) return 0; - b_log(B_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno)); - return -1; - } - return 1; -} - -#endif +../../b/b.h
\ No newline at end of file |
