added: `onyx watch` to continuously rebuild
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 26 Apr 2023 03:29:01 +0000 (22:29 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 26 Apr 2023 03:29:01 +0000 (22:29 -0500)
compiler/include/astnodes.h
compiler/src/onyx.c
shared/include/bh.h

index 59bfa39b0a4b7651109ad5007c102482c498b554..9a76ef8c3121b4cf5d9ae6221935a9ecb58b040e 100644 (file)
@@ -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,
 };
index 59668dd3fc209650394ebe80d9aab80fd8d287ba..deb0143d441b5b8932e6d9a9b3d68f756df7777a 100644 (file)
@@ -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 <input files>\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 <signal.h>
+
+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);
index 8c95ed72d961896b05a06b47bcd89aa361d9a70f..10300d9643816371e4a0c3ca0ee7b50e7daf1559 100644 (file)
@@ -37,6 +37,8 @@
     #include <unistd.h>
     #include <dirent.h>
     #include <pthread.h>
+    #include <sys/inotify.h>
+    #include <sys/select.h>
 #endif
 
 #include <stdlib.h>
@@ -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