Commit a7b6fa5bee

Andrew Kelley <superjoe30@gmail.com>
2016-02-17 23:56:06
os: implement windows os layer
1 parent 06398a2
Changed files (4)
src/link.cpp
@@ -802,7 +802,11 @@ void codegen_link(CodeGen *g, const char *out_file) {
     int return_code;
     Buf ld_stderr = BUF_INIT;
     Buf ld_stdout = BUF_INIT;
-    os_exec_process(buf_ptr(g->linker_path), lj.args, &return_code, &ld_stderr, &ld_stdout);
+    int err = os_exec_process(buf_ptr(g->linker_path), lj.args, &return_code, &ld_stderr, &ld_stdout);
+    if (err) {
+        fprintf(stderr, "linker not found: '%s'\n", buf_ptr(g->linker_path));
+        exit(1);
+    }
 
     if (return_code != 0) {
         fprintf(stderr, "linker failed with return code %d\n", return_code);
src/main.cpp
@@ -98,6 +98,8 @@ enum Cmd {
 };
 
 int main(int argc, char **argv) {
+    os_init();
+
     char *arg0 = argv[0];
     Cmd cmd = CmdInvalid;
     const char *in_file = nullptr;
src/os.cpp
@@ -24,10 +24,6 @@
 #define WIN32_LEAN_AND_MEAN
 #endif
 
-#if !defined(UNICODE)
-#define UNICODE
-#endif
-
 #include <windows.h>
 #include <io.h>
 #else
@@ -44,6 +40,11 @@
 
 #include <stdlib.h>
 #include <errno.h>
+#include <time.h>
+
+// these implementations are lazy. But who cares, we'll make a robust
+// implementation in the zig standard library and then this code all gets
+// deleted when we self-host. it works for now.
 
 
 #if defined(ZIG_OS_POSIX)
@@ -70,7 +71,13 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,
 
 #if defined(ZIG_OS_WINDOWS)
 static void os_spawn_process_windows(const char *exe, ZigList<const char *> &args, int *return_code) {
-    zig_panic("TODO os_spawn_process_windows");
+    Buf stderr_buf = BUF_INIT;
+    Buf stdout_buf = BUF_INIT;
+
+    // TODO this is supposed to inherit stdout/stderr instead of capturing it
+    os_exec_process(exe, args, return_code, &stderr_buf, &stdout_buf);
+    fwrite(buf_ptr(&stderr_buf), 1, buf_len(&stderr_buf), stderr);
+    fwrite(buf_ptr(&stdout_buf), 1, buf_len(&stdout_buf), stdout);
 }
 #endif
 
@@ -111,7 +118,12 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
 
 int os_path_real(Buf *rel_path, Buf *out_abs_path) {
 #if defined(ZIG_OS_WINDOWS)
-    zig_panic("TODO os_path_real for windows");
+    buf_resize(out_abs_path, 4096);
+    if (_fullpath(buf_ptr(out_abs_path), buf_ptr(rel_path), buf_len(out_abs_path)) == nullptr) {
+        zig_panic("_fullpath failed");
+    }
+    buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path)));
+    return ErrorNone;
 #elif defined(ZIG_OS_POSIX)
     buf_resize(out_abs_path, PATH_MAX + 1);
     char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path));
@@ -157,7 +169,7 @@ int os_fetch_file(FILE *f, Buf *out_buf) {
 
 
 #if defined(ZIG_OS_POSIX)
-static void os_exec_process_posix(const char *exe, ZigList<const char *> &args,
+static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,
         int *return_code, Buf *out_stderr, Buf *out_stdout)
 {
     int stdin_pipe[2];
@@ -193,7 +205,11 @@ static void os_exec_process_posix(const char *exe, ZigList<const char *> &args,
             argv[i + 1] = args.at(i);
         }
         execvp(exe, const_cast<char * const *>(argv));
-        zig_panic("execvp failed: %s", strerror(errno));
+        if (errno == ENOENT) {
+            return ErrorFileNotFound;
+        } else {
+            zig_panic("execvp failed: %s", strerror(errno));
+        }
     } else {
         // parent
         close(stdin_pipe[0]);
@@ -205,19 +221,158 @@ static void os_exec_process_posix(const char *exe, ZigList<const char *> &args,
         os_fetch_file(fdopen(stdout_pipe[0], "rb"), out_stdout);
         os_fetch_file(fdopen(stderr_pipe[0], "rb"), out_stderr);
 
+        return 0;
     }
 }
 #endif
 
 #if defined(ZIG_OS_WINDOWS)
-static void os_exec_process_windows(const char *exe, ZigList<const char *> &args,
+
+/*
+static void win32_panic(const char *str) {
+    DWORD err = GetLastError();
+    LPSTR messageBuffer = nullptr;
+    FormatMessageA(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
+    zig_panic(str, messageBuffer);
+    LocalFree(messageBuffer);
+}
+*/
+
+static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,
         int *return_code, Buf *out_stderr, Buf *out_stdout)
 {
-    zig_panic("TODO implement os_exec_process_windows");
+    Buf command_line = BUF_INIT;
+    buf_resize(&command_line, 0);
+
+    buf_append_char(&command_line, '\"');
+    buf_append_str(&command_line, exe);
+    buf_append_char(&command_line, '\"');
+
+    for (int arg_i = 0; arg_i < args.length; arg_i += 1) {
+        buf_append_str(&command_line, " \"");
+        const char *arg = args.at(arg_i);
+        int arg_len = strlen(arg);
+        for (int c_i = 0; c_i < arg_len; c_i += 1) {
+            if (arg[c_i] == '\"') {
+                zig_panic("TODO");
+            }
+            buf_append_char(&command_line, arg[c_i]);
+        }
+        buf_append_char(&command_line, '\"');
+    }
+
+
+    HANDLE g_hChildStd_IN_Rd = NULL;
+    HANDLE g_hChildStd_IN_Wr = NULL;
+    HANDLE g_hChildStd_OUT_Rd = NULL;
+    HANDLE g_hChildStd_OUT_Wr = NULL;
+    HANDLE g_hChildStd_ERR_Rd = NULL;
+    HANDLE g_hChildStd_ERR_Wr = NULL;
+
+    SECURITY_ATTRIBUTES saAttr;
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saAttr.bInheritHandle = TRUE;
+    saAttr.lpSecurityDescriptor = NULL;
+
+    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
+        zig_panic("StdoutRd CreatePipe");
+    }
+
+    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
+        zig_panic("Stdout SetHandleInformation");
+    }
+
+    if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) {
+        zig_panic("stderr CreatePipe");
+    }
+
+    if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
+        zig_panic("stderr SetHandleInformation");
+    }
+
+    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) {
+        zig_panic("Stdin CreatePipe");
+    }
+
+    if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) {
+        zig_panic("Stdin SetHandleInformation");
+    }
+
+
+    PROCESS_INFORMATION piProcInfo = {0};
+    STARTUPINFO siStartInfo = {0};
+    siStartInfo.cb = sizeof(STARTUPINFO);
+    siStartInfo.hStdError = g_hChildStd_ERR_Wr;
+    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
+    siStartInfo.hStdInput = g_hChildStd_IN_Rd;
+    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+    BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
+            &siStartInfo, &piProcInfo);
+
+    if (!success) {
+        if (GetLastError() == ERROR_FILE_NOT_FOUND) {
+            CloseHandle(piProcInfo.hProcess);
+            CloseHandle(piProcInfo.hThread);
+            return ErrorFileNotFound;
+        }
+        zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line));
+    }
+
+    if (!CloseHandle(g_hChildStd_IN_Wr)) {
+        zig_panic("stdinwr closehandle");
+    }
+
+    CloseHandle(g_hChildStd_IN_Rd);
+    CloseHandle(g_hChildStd_ERR_Wr);
+    CloseHandle(g_hChildStd_OUT_Wr);
+
+    static const int BUF_SIZE = 4 * 1024;
+    {
+        DWORD dwRead;
+        char chBuf[BUF_SIZE];
+
+        buf_resize(out_stdout, 0);
+        for (;;) {
+            success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL);
+            if (!success || dwRead == 0) break;
+
+            buf_append_mem(out_stdout, chBuf, dwRead);
+        }
+        CloseHandle(g_hChildStd_OUT_Rd);
+    }
+    {
+        DWORD dwRead;
+        char chBuf[BUF_SIZE];
+
+        buf_resize(out_stderr, 0);
+        for (;;) {
+            success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL);
+            if (!success || dwRead == 0) break;
+
+            buf_append_mem(out_stderr, chBuf, dwRead);
+        }
+        CloseHandle(g_hChildStd_ERR_Rd);
+    }
+
+    WaitForSingleObject(piProcInfo.hProcess, INFINITE);
+
+    DWORD exit_code;
+    if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
+        zig_panic("GetExitCodeProcess failed");
+    }
+    *return_code = exit_code;
+
+    CloseHandle(piProcInfo.hProcess);
+    CloseHandle(piProcInfo.hThread);
+
+    return 0;
 }
 #endif
 
-void os_exec_process(const char *exe, ZigList<const char *> &args,
+int os_exec_process(const char *exe, ZigList<const char *> &args,
         int *return_code, Buf *out_stderr, Buf *out_stdout)
 {
 #if defined(ZIG_OS_WINDOWS)
@@ -267,7 +422,11 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents) {
 
 int os_get_cwd(Buf *out_cwd) {
 #if defined(ZIG_OS_WINDOWS)
-    zig_panic("TODO os_get_cwd for windows");
+    buf_resize(out_cwd, 4096);
+    if (GetCurrentDirectory(buf_len(out_cwd), buf_ptr(out_cwd)) == 0) {
+        zig_panic("GetCurrentDirectory failed");
+    }
+    return 0;
 #elif defined(ZIG_OS_POSIX)
     int err = ERANGE;
     buf_resize(out_cwd, 512);
@@ -296,8 +455,12 @@ bool os_stderr_tty(void) {
 
 #if defined(ZIG_OS_POSIX)
 static int os_buf_to_tmp_file_posix(Buf *contents, Buf *suffix, Buf *out_tmp_path) {
+    const char *tmp_dir = getenv("TMPDIR");
+    if (!tmp_dir) {
+        tmp_dir = P_tmpdir;
+    }
     buf_resize(out_tmp_path, 0);
-    buf_appendf(out_tmp_path, "/tmp/XXXXXX%s", buf_ptr(suffix));
+    buf_appendf(out_tmp_path, "%s/XXXXXX%s", tmp_dir, buf_ptr(suffix));
 
     int fd = mkstemps(buf_ptr(out_tmp_path), buf_len(suffix));
     if (fd < 0) {
@@ -321,7 +484,35 @@ static int os_buf_to_tmp_file_posix(Buf *contents, Buf *suffix, Buf *out_tmp_pat
 
 #if defined(ZIG_OS_WINDOWS)
 static int os_buf_to_tmp_file_windows(Buf *contents, Buf *suffix, Buf *out_tmp_path) {
-    zig_panic("TODO implement os_buf_to_tmp_file_windows");
+    char tmp_dir[MAX_PATH + 1];
+    if (GetTempPath(MAX_PATH, tmp_dir) == 0) {
+        zig_panic("GetTempPath failed");
+    }
+    buf_init_from_str(out_tmp_path, tmp_dir);
+
+    const char base64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
+    assert(array_length(base64) == 64 + 1);
+    for (int i = 0; i < 8; i += 1) {
+        buf_append_char(out_tmp_path, base64[rand() % 64]);
+    }
+
+    buf_append_buf(out_tmp_path, suffix);
+
+    FILE *f = fopen(buf_ptr(out_tmp_path), "wb");
+
+    if (!f) {
+        zig_panic("unable to open %s: %s", buf_ptr(out_tmp_path), strerror(errno));
+    }
+
+    size_t amt_written = fwrite(buf_ptr(contents), 1, buf_len(contents), f);
+    if (amt_written != (size_t)buf_len(contents)) {
+        zig_panic("write failed: %s", strerror(errno));
+    }
+
+    if (fclose(f)) {
+        zig_panic("fclose failed");
+    }
+    return 0;
 }
 #endif
 
@@ -342,3 +533,7 @@ int os_delete_file(Buf *path) {
         return 0;
     }
 }
+
+void os_init(void) {
+    srand(time(NULL));
+}
src/os.hpp
@@ -13,8 +13,9 @@
 
 #include <stdio.h>
 
+void os_init(void);
 void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code);
-void os_exec_process(const char *exe, ZigList<const char *> &args,
+int os_exec_process(const char *exe, ZigList<const char *> &args,
         int *return_code, Buf *out_stderr, Buf *out_stdout);
 
 void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);