Browse Source

Implement GO_REBUILD_URSELF

rexim 4 years ago
parent
commit
bddad27087
3 changed files with 86 additions and 1 deletions
  1. 1 0
      .gitignore
  2. 3 1
      nobuild.c
  3. 82 0
      nobuild.h

+ 1 - 0
.gitignore

@@ -1,2 +1,3 @@
 nobuild
+*.old
 output.txt

+ 3 - 1
nobuild.c

@@ -52,8 +52,10 @@ void print_chain(const Chain *chain)
     });
 }
 
-int main(void)
+int main(int argc, char **argv)
 {
+    GO_REBUILD_URSELF(argc, argv);
+
     build_tools();
     run_examples();
 

+ 82 - 0
nobuild.h

@@ -200,6 +200,31 @@ void chain_echo(Chain chain);
         chain_run_sync(chain);                                          \
     } while(0)
 
+// TODO: REBUILD_URSELF does not distinguish MSVC and MinGW setups on Windows
+#ifndef REBUILD_URSELF
+#  if _WIN32
+#    define REBUILD_URSELF(binary_path, source_path) CMD("cl.exe", source_path)
+#  else
+#    define REBUILD_URSELF(binary_path, source_path) CMD("cc", "-o", binary_path, source_path)
+#  endif
+#endif
+
+#define GO_REBUILD_URSELF(argc, argv)                           \
+    do {                                                        \
+        const char *source_path = __FILE__;                     \
+        assert(argc >= 1);                                      \
+        const char *binary_path = argv[0];                      \
+                                                                \
+        if (is_path1_modified_after_path2(source_path, binary_path)) {  \
+            RENAME(binary_path, CONCAT(binary_path, ".old"));   \
+            REBUILD_URSELF(binary_path, source_path);           \
+            CMD(binary_path);                                   \
+            exit(0);                                            \
+        }                                                       \
+    } while(0)
+
+void rebuild_urself(const char *binary_path, const char *source_path);
+
 int path_is_dir(Cstr path);
 #define IS_DIR(path) path_is_dir(path)
 
@@ -214,6 +239,13 @@ void path_mkdirs(Cstr_Array path);
         path_mkdirs(path);                                      \
     } while (0)
 
+void path_rename(Cstr old_path, Cstr new_path);
+#define RENAME(old_path, new_path)                    \
+    do {                                              \
+        INFO("RENAME: %s -> %s", old_path, new_path); \
+        path_rename(old_path, new_path);              \
+    } while (0)
+
 void path_rm(Cstr path);
 #define RM(path)                                \
     do {                                        \
@@ -909,6 +941,21 @@ int path_is_dir(Cstr path)
 #endif // _WIN32
 }
 
+void path_rename(const char *old_path, const char *new_path)
+{
+#ifdef _WIN32
+    if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) {
+        PANIC("could not rename %s to %s: %s", old_path, new_path,
+              GetLastErrorAsString());
+    }
+#else
+    if (rename(old_path, new_path) < 0) {
+        PANIC("could not rename %s to %s: %s", old_path, new_path,
+              strerror(errno));
+    }
+#endif // _WIN32
+}
+
 void path_mkdirs(Cstr_Array path)
 {
     if (path.count == 0) {
@@ -977,6 +1024,41 @@ void path_rm(Cstr path)
     }
 }
 
+int is_path1_modified_after_path2(const char *path1, const char *path2)
+{
+#ifdef _WIN32
+    FILETIME path1_time, path2_time;
+
+    Fd path1_fd = fd_open_for_read(path1);
+    if (!GetFileTime(path1_fd, NULL, NULL, &path1_time)) {
+        PANIC("could not get time of %s: %s", path1, GetLastErrorAsString());
+    }
+    fd_close(path1_fd);
+
+    Fd path2_fd = fd_open_for_read(path2);
+    if (!GetFileTime(path2_fd, NULL, NULL, &path2_time)) {
+        PANIC("could not get time of %s: %s", path2, GetLastErrorAsString());
+    }
+    fd_close(path2_fd);
+
+    return CompareFileTime(&path1, &path2) == 1;
+#else
+    struct stat statbuf = {0};
+
+    if (stat(path1, &statbuf) < 0) {
+        PANIC("could not stat %s: %s\n", path1, strerror(errno));
+    }
+    int path1_time = statbuf.st_mtime;
+
+    if (stat(path2, &statbuf) < 0) {
+        PANIC("could not stat %s: %s\n", path2, strerror(errno));
+    }
+    int path2_time = statbuf.st_mtime;
+
+    return path1_time > path2_time;
+#endif
+}
+
 void VLOG(FILE *stream, Cstr tag, Cstr fmt, va_list args)
 {
     fprintf(stream, "[%s] ", tag);