From e1662fff6538e0cb7f55b83d1eb5b8400d11defa Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 25 Apr 2023 22:29:01 -0500 Subject: [PATCH] added: `onyx watch` to continuously rebuild --- compiler/include/astnodes.h | 1 + compiler/src/onyx.c | 74 ++++++++++++++++++++++++++++++++++++- shared/include/bh.h | 71 ++++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 2 deletions(-) diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index 59bfa39b..9a76ef8c 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -1669,6 +1669,7 @@ enum CompileAction { ONYX_COMPILE_ACTION_CHECK, ONYX_COMPILE_ACTION_RUN, ONYX_COMPILE_ACTION_RUN_WASM, + ONYX_COMPILE_ACTION_WATCH, ONYX_COMPILE_ACTION_DOCUMENT, ONYX_COMPILE_ACTION_PRINT_HELP, }; diff --git a/compiler/src/onyx.c b/compiler/src/onyx.c index 59668dd3..deb0143d 100644 --- a/compiler/src/onyx.c +++ b/compiler/src/onyx.c @@ -42,6 +42,7 @@ static const char* top_level_docstring = DOCSTRING_HEADER "\trun Compiles and runs an Onyx program, all at once.\n" #endif "\tcheck Checks syntax and types of an Onyx program.\n" + "\twatch Continuously rebuilds an Onyx program on file changes.\n" "\tpackage Package manager\n"; // "\tdoc \n" @@ -159,6 +160,12 @@ static CompileOptions compile_opts_parse(bh_allocator alloc, int argc, char *arg arg_parse_start = 2; } #endif + #ifdef _BH_LINUX + else if (!strcmp(argv[1], "watch")) { + options.action = ONYX_COMPILE_ACTION_WATCH; + arg_parse_start = 2; + } + #endif else { bh_printf("Unknown subcommand: '%s'\n", argv[1]); bh_printf("Run \"onyx help\" for valid subcommands.\n"); @@ -286,7 +293,8 @@ static void compile_opts_free(CompileOptions* opts) { static void print_subcommand_help(const char *subcommand) { if (!strcmp(subcommand, "build") || !strcmp(subcommand, "run") - || !strcmp(subcommand, "check")) { + || !strcmp(subcommand, "check") + || !strcmp(subcommand, "watch")) { bh_printf(build_docstring, subcommand); } @@ -935,6 +943,66 @@ void cleanup_compilation() { bh_managed_heap_free(&mh); } +#ifdef _BH_LINUX + +#include + +static bh_file_watch watches; + +static void onyx_watch_stop(int sig) { + bh_file_watch_stop(&watches); +} + +static void onyx_watch(CompileOptions *compile_opts) { + signal(SIGINT, onyx_watch_stop); + + b32 running_watch = 1; + + do { + bh_printf("\e[2J\e[?25l\n"); + bh_printf("\e[3;1H"); + + if (do_compilation(compile_opts) == ONYX_COMPILER_PROGRESS_SUCCESS) { + onyx_flush_module(); + bh_printf("\e[92mNo errors.\n"); + } + + char time_buf[128] = {0}; + time_t now = time(NULL); + strftime(time_buf, 128, "%X", localtime(&now)); + bh_printf("\e[1;1H\e[30;104m [BUILT] %s \e[0m", time_buf); + + i32 errors = bh_arr_length(context.errors.errors); + if (errors == 0) { + bh_printf("\e[30;102m [ERRORS] 0 \e[0m"); + } else { + bh_printf("\e[30;101m [ERRORS] %d \e[0m", errors); + } + + watches = bh_file_watch_new(); + + bh_arr_each(bh_file_contents, file, context.loaded_files) { + bh_file_watch_add(&watches, file->filename); + } + + cleanup_compilation(); + + if (!bh_file_watch_wait(&watches)) { + running_watch = 0; + } + + bh_file_watch_free(&watches); + } while(running_watch); + + + bh_printf("\e[2J\e[1;1H\e[?25h\n"); +} + +#endif + + + + int main(int argc, char *argv[]) { CompileOptions compile_opts = compile_opts_parse(bh_heap_allocator(), argc, argv); @@ -960,6 +1028,10 @@ int main(int argc, char *argv[]) { } break; + case ONYX_COMPILE_ACTION_WATCH: + onyx_watch(&compile_opts); + break; + #ifdef ENABLE_RUN_WITH_WASMER case ONYX_COMPILE_ACTION_RUN: compiler_progress = do_compilation(&compile_opts); diff --git a/shared/include/bh.h b/shared/include/bh.h index 8c95ed72..10300d96 100644 --- a/shared/include/bh.h +++ b/shared/include/bh.h @@ -37,6 +37,8 @@ #include #include #include + #include + #include #endif #include @@ -487,7 +489,25 @@ void bh_dir_close(bh_dir dir); -b32 bh_wait_for_changes_in_directories(u32 count, char ** paths); +#ifdef _BH_LINUX + typedef struct bh_file_watch { + int inotify_fd; + int kill_pipe[2]; + + fd_set fds; + } bh_file_watch; +#endif +#ifdef _BH_WINDOWS + // TODO: Make these work on Windows + typedef u32 bh_file_watch; +#endif + +bh_file_watch bh_file_watch_new(); +void bh_file_watch_free(bh_file_watch *w); +void bh_file_watch_add(bh_file_watch *w, const char *filename); +b32 bh_file_watch_wait(bh_file_watch *w); +void bh_file_watch_stop(bh_file_watch *w); + #endif @@ -2095,6 +2115,55 @@ void bh_dir_close(bh_dir dir) { #undef DIR_SEPARATOR +#ifdef _BH_LINUX + +bh_file_watch bh_file_watch_new() { + // TODO: Proper error checking + bh_file_watch w; + assert(pipe(w.kill_pipe) != -1); + + assert((w.inotify_fd = inotify_init()) != -1); + + FD_ZERO(&w.fds); + FD_SET(w.inotify_fd, &w.fds); + FD_SET(w.kill_pipe[0], &w.fds); + + return w; +} + +void bh_file_watch_free(bh_file_watch *w) { + close(w->inotify_fd); + close(w->kill_pipe[0]); + close(w->kill_pipe[1]); +} + +void bh_file_watch_add(bh_file_watch *w, const char *filename) { + inotify_add_watch(w->inotify_fd, filename, IN_MODIFY); +} + +b32 bh_file_watch_wait(bh_file_watch *w) { + select(FD_SETSIZE, &w->fds, NULL, NULL, NULL); + + if (FD_ISSET(w->kill_pipe[0], &w->fds)) { + char buf; + read(w->kill_pipe[0], &buf, sizeof(buf)); + return 0; + } + + FD_ZERO(&w->fds); + FD_SET(w->inotify_fd, &w->fds); + FD_SET(w->kill_pipe[0], &w->fds); + + return 1; +} + +void bh_file_watch_stop(bh_file_watch *w) { + char buf = 'a'; + write(w->kill_pipe[1], &buf, 1); +} + +#endif // ifdef _BH_LINUX + #endif // ifndef BH_NO_FILE -- 2.25.1