Browse Source

Merge pull request #27 from tsoding/go-rebuild-urself

Implement GO_REBUILD_URSELF
Alexey Kutepov 4 years ago
parent
commit
f476402b7d
3 changed files with 91 additions and 5 deletions
  1. 1 0
      .gitignore
  2. 3 1
      nobuild.c
  3. 87 4
      nobuild.h

+ 1 - 0
.gitignore

@@ -1,2 +1,3 @@
 nobuild
 nobuild
+*.old
 output.txt
 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();
     build_tools();
     run_examples();
     run_examples();
 
 

+ 87 - 4
nobuild.h

@@ -72,7 +72,7 @@ int closedir(DIR *dirp);
 #endif  // MINIRENT_H_
 #endif  // MINIRENT_H_
 // minirent.h HEADER END ////////////////////////////////////////
 // minirent.h HEADER END ////////////////////////////////////////
 
 
-// TODO: use GetLastErrorAsString everywhere on Windows error reporting
+// TODO(#28): use GetLastErrorAsString everywhere on Windows error reporting
 LPSTR GetLastErrorAsString(void);
 LPSTR GetLastErrorAsString(void);
 
 
 #endif  // _WIN32
 #endif  // _WIN32
@@ -200,6 +200,32 @@ void chain_echo(Chain chain);
         chain_run_sync(chain);                                          \
         chain_run_sync(chain);                                          \
     } while(0)
     } while(0)
 
 
+// TODO(#29): 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
+
+// TODO(#30): we need to test GO_REBUILD_URSELF on a real Windows machine
+#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);
 int path_is_dir(Cstr path);
 #define IS_DIR(path) path_is_dir(path)
 #define IS_DIR(path) path_is_dir(path)
 
 
@@ -214,6 +240,13 @@ void path_mkdirs(Cstr_Array path);
         path_mkdirs(path);                                      \
         path_mkdirs(path);                                      \
     } while (0)
     } 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);
 void path_rm(Cstr path);
 #define RM(path)                                \
 #define RM(path)                                \
     do {                                        \
     do {                                        \
@@ -622,7 +655,7 @@ void pid_wait(Pid pid)
 
 
 Cstr cmd_show(Cmd cmd)
 Cstr cmd_show(Cmd cmd)
 {
 {
-    // TODO: cmd_show does not render the command line properly
+    // TODO(#31): cmd_show does not render the command line properly
     // - No string literals when arguments contains space
     // - No string literals when arguments contains space
     // - No escaping of special characters
     // - No escaping of special characters
     // - Etc.
     // - Etc.
@@ -640,7 +673,7 @@ Pid cmd_run_async(Cmd cmd, Fd *fdin, Fd *fdout)
     // NOTE: theoretically setting NULL to std handles should not be a problem
     // NOTE: theoretically setting NULL to std handles should not be a problem
     // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior
     // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior
     siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
     siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
-    // TODO: check for errors in GetStdHandle
+    // TODO(#32): check for errors in GetStdHandle
     siStartInfo.hStdOutput = fdout ? *fdout : GetStdHandle(STD_OUTPUT_HANDLE);
     siStartInfo.hStdOutput = fdout ? *fdout : GetStdHandle(STD_OUTPUT_HANDLE);
     siStartInfo.hStdInput = fdin ? *fdin : GetStdHandle(STD_INPUT_HANDLE);
     siStartInfo.hStdInput = fdin ? *fdin : GetStdHandle(STD_INPUT_HANDLE);
     siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
     siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
@@ -651,7 +684,7 @@ Pid cmd_run_async(Cmd cmd, Fd *fdin, Fd *fdout)
     BOOL bSuccess =
     BOOL bSuccess =
         CreateProcess(
         CreateProcess(
             NULL,
             NULL,
-            // TODO: cmd_run_async on Windows does not render command line properly
+            // TODO(#33): cmd_run_async on Windows does not render command line properly
             // It may require wrapping some arguments with double-quotes if they contains spaces, etc.
             // It may require wrapping some arguments with double-quotes if they contains spaces, etc.
             cstr_array_join(" ", cmd.line),
             cstr_array_join(" ", cmd.line),
             NULL,
             NULL,
@@ -909,6 +942,21 @@ int path_is_dir(Cstr path)
 #endif // _WIN32
 #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)
 void path_mkdirs(Cstr_Array path)
 {
 {
     if (path.count == 0) {
     if (path.count == 0) {
@@ -977,6 +1025,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)
 void VLOG(FILE *stream, Cstr tag, Cstr fmt, va_list args)
 {
 {
     fprintf(stream, "[%s] ", tag);
     fprintf(stream, "[%s] ", tag);