commit cd24673c722c7c2e23062175043c8fd3eb9b6df4
parent f9bfe3fe931e96825fa793075b9ee35a47429514
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 18 Jun 2026 08:43:22 -0700
Add Windows build coordinator host env
Diffstat:
2 files changed, 863 insertions(+), 1 deletion(-)
diff --git a/driver/env/build_host_windows.c b/driver/env/build_host_windows.c
@@ -0,0 +1,862 @@
+#include "../env_build.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601
+#endif
+#include <windows.h>
+
+#include "dist_host.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DRIVER_BUILD_FRAME_MAX 65536u
+#define DRIVER_BUILD_PIPE_TIMEOUT_MS 10u
+
+struct KitBuildProc {
+ HANDLE process;
+};
+
+struct KitBuildListener {
+ char name[KIT_BUILD_PATH_MAX];
+ HANDLE pipe;
+ HANDLE event;
+ OVERLAPPED ov;
+ int connected;
+};
+
+struct KitBuildConn {
+ HANDLE h;
+};
+
+static wchar_t* db_widen(const char* utf8) {
+ int need;
+ wchar_t* w;
+ if (!utf8 || !*utf8) return NULL;
+ need = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
+ if (need <= 0) return NULL;
+ w = (wchar_t*)malloc((size_t)need * sizeof(*w));
+ if (!w) return NULL;
+ if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, w, need) <= 0) {
+ free(w);
+ return NULL;
+ }
+ return w;
+}
+
+static char* db_narrow(const wchar_t* wide) {
+ int need;
+ char* out;
+ if (!wide) return NULL;
+ need = WideCharToMultiByte(CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL);
+ if (need <= 0) return NULL;
+ out = (char*)malloc((size_t)need);
+ if (!out) return NULL;
+ if (WideCharToMultiByte(CP_UTF8, 0, wide, -1, out, need, NULL, NULL) <= 0) {
+ free(out);
+ return NULL;
+ }
+ return out;
+}
+
+static char* db_slice_dup(DriverEnv* env, KitSlice s) {
+ char* out = (char*)driver_alloc(env, s.len + 1u);
+ if (!out) return NULL;
+ if (s.len) memcpy(out, s.s, s.len);
+ out[s.len] = '\0';
+ return out;
+}
+
+static int db_path_copy(char* out, size_t cap, KitSlice s) {
+ if (!out || cap == 0u || !s.s || s.len + 1u > cap) return 1;
+ memcpy(out, s.s, s.len);
+ out[s.len] = '\0';
+ return 0;
+}
+
+static int db_is_abs_path(const char* path) {
+ if (!path || !path[0]) return 0;
+ if ((path[0] == '/' || path[0] == '\\')) return 1;
+ return ((path[0] >= 'A' && path[0] <= 'Z') ||
+ (path[0] >= 'a' && path[0] <= 'z')) &&
+ path[1] == ':';
+}
+
+static char* db_cwd_join(DriverEnv* env, KitSlice path) {
+ DWORD need;
+ wchar_t* wpath;
+ wchar_t* wbuf = NULL;
+ char* p;
+ char* out;
+ size_t n;
+ if (!path.s || path.len == 0u) return NULL;
+ p = db_slice_dup(env, path);
+ if (!p) return NULL;
+ if (db_is_abs_path(p)) return p;
+ wpath = db_widen(p);
+ driver_free(env, p, path.len + 1u);
+ if (!wpath) return NULL;
+ need = GetFullPathNameW(wpath, 0, NULL, NULL);
+ if (need == 0) {
+ free(wpath);
+ return NULL;
+ }
+ wbuf = (wchar_t*)malloc((size_t)need * sizeof(*wbuf));
+ if (!wbuf || GetFullPathNameW(wpath, need, wbuf, NULL) == 0) {
+ free(wbuf);
+ free(wpath);
+ return NULL;
+ }
+ free(wpath);
+ p = db_narrow(wbuf);
+ free(wbuf);
+ if (!p) return NULL;
+ n = strlen(p) + 1u;
+ out = (char*)driver_alloc(env, n);
+ if (out) memcpy(out, p, n);
+ free(p);
+ return out;
+}
+
+static int db_store_rename(void* user, KitSlice from, KitSlice to) {
+ char f[KIT_BUILD_PATH_MAX], t[KIT_BUILD_PATH_MAX];
+ wchar_t *wf, *wt;
+ BOOL ok;
+ (void)user;
+ if (db_path_copy(f, sizeof f, from) || db_path_copy(t, sizeof t, to))
+ return 1;
+ wf = db_widen(f);
+ wt = db_widen(t);
+ if (!wf || !wt) {
+ free(wf);
+ free(wt);
+ return 1;
+ }
+ ok = MoveFileExW(wf, wt, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
+ free(wf);
+ free(wt);
+ return ok ? 0 : 1;
+}
+
+static int db_store_remove(void* user, KitSlice path, int recursive) {
+ char p[KIT_BUILD_PATH_MAX];
+ wchar_t* wp;
+ BOOL ok;
+ DWORD err;
+ (void)user;
+ if (db_path_copy(p, sizeof p, path)) return 1;
+ if (recursive) return driver_remove_tree(p);
+ wp = db_widen(p);
+ if (!wp) return 1;
+ ok = DeleteFileW(wp);
+ if (!ok) ok = RemoveDirectoryW(wp);
+ err = ok ? 0 : GetLastError();
+ free(wp);
+ return ok || err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND ? 0
+ : 1;
+}
+
+static int db_store_make_temp_dir(void* user, KitSlice parent, char* out,
+ size_t cap) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ char p[KIT_BUILD_PATH_MAX];
+ size_t i, n;
+ if (!bh || !bh->env || !out || cap == 0u ||
+ db_path_copy(p, sizeof p, parent))
+ return 1;
+ n = strlen(p);
+ for (i = 0; i < 128u; ++i) {
+ wchar_t* wout;
+ int len = snprintf(out, cap, "%s%stmp.%lu.%llu", p,
+ n && p[n - 1u] != '/' && p[n - 1u] != '\\' ? "\\" : "",
+ (unsigned long)GetCurrentProcessId(),
+ (unsigned long long)bh->next_endpoint++);
+ if (len < 0 || (size_t)len >= cap) return 1;
+ wout = db_widen(out);
+ if (!wout) return 1;
+ if (CreateDirectoryW(wout, NULL)) {
+ free(wout);
+ return 0;
+ }
+ if (GetLastError() != ERROR_ALREADY_EXISTS) {
+ free(wout);
+ return 1;
+ }
+ free(wout);
+ }
+ return 1;
+}
+
+static int db_store_sync_path(void* user, KitSlice path) {
+ char p[KIT_BUILD_PATH_MAX];
+ wchar_t* wp;
+ HANDLE h;
+ int rc = 0;
+ (void)user;
+ if (db_path_copy(p, sizeof p, path)) return 1;
+ wp = db_widen(p);
+ if (!wp) return 1;
+ h = CreateFileW(wp, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ free(wp);
+ if (h == INVALID_HANDLE_VALUE) return 0;
+ if (!FlushFileBuffers(h)) rc = 1;
+ CloseHandle(h);
+ return rc;
+}
+
+static int db_store_list_dir(void* user, KitSlice path, KitBuildDirFn cb,
+ void* cb_user) {
+ char p[KIT_BUILD_PATH_MAX];
+ char pattern[KIT_BUILD_PATH_MAX];
+ wchar_t* wpattern;
+ WIN32_FIND_DATAW fd;
+ HANDLE h;
+ DWORD last;
+ (void)user;
+ if (!cb || db_path_copy(p, sizeof p, path)) return 1;
+ if ((size_t)snprintf(pattern, sizeof pattern, "%s\\*", p) >=
+ sizeof pattern)
+ return 1;
+ wpattern = db_widen(pattern);
+ if (!wpattern) return 1;
+ h = FindFirstFileW(wpattern, &fd);
+ free(wpattern);
+ if (h == INVALID_HANDLE_VALUE) {
+ last = GetLastError();
+ return last == ERROR_FILE_NOT_FOUND ? 0 : 1;
+ }
+ for (;;) {
+ char* name = db_narrow(fd.cFileName);
+ KitSlice s;
+ if (!name) {
+ FindClose(h);
+ return 1;
+ }
+ if (driver_streq(name, ".") || driver_streq(name, "..")) {
+ free(name);
+ goto next;
+ }
+ s.s = name;
+ s.len = strlen(name);
+ if (cb(cb_user, s)) {
+ free(name);
+ FindClose(h);
+ return 1;
+ }
+ free(name);
+ next:
+ if (!FindNextFileW(h, &fd)) break;
+ }
+ last = GetLastError();
+ FindClose(h);
+ return last == ERROR_NO_MORE_FILES ? 0 : 1;
+}
+
+static void db_free_strv(DriverEnv* env, char** v, size_t n) {
+ size_t i;
+ if (!v) return;
+ for (i = 0; i < n; ++i)
+ if (v[i]) driver_free(env, v[i], strlen(v[i]) + 1u);
+ driver_free(env, v, (n + 1u) * sizeof *v);
+}
+
+static wchar_t** db_widen_strv(char** v, size_t n) {
+ wchar_t** out;
+ size_t i;
+ out = (wchar_t**)calloc(n + 1u, sizeof(*out));
+ if (!out) return NULL;
+ for (i = 0; i < n; ++i) {
+ out[i] = db_widen(v[i]);
+ if (!out[i]) {
+ while (i > 0u) free(out[--i]);
+ free(out);
+ return NULL;
+ }
+ }
+ return out;
+}
+
+static void db_free_wstrv(wchar_t** v, size_t n) {
+ size_t i;
+ if (!v) return;
+ for (i = 0; i < n; ++i) free(v[i]);
+ free(v);
+}
+
+static int db_arg_needs_quote(const wchar_t* s) {
+ if (!s || !s[0]) return 1;
+ for (; *s; ++s)
+ if (*s == L' ' || *s == L'\t' || *s == L'"') return 1;
+ return 0;
+}
+
+static size_t db_arg_quoted_len(const wchar_t* s) {
+ size_t len = 0;
+ size_t slash = 0;
+ int quote = db_arg_needs_quote(s);
+ if (!quote) return (size_t)lstrlenW(s);
+ len = 2u;
+ for (; *s; ++s) {
+ if (*s == L'\\') {
+ ++slash;
+ ++len;
+ } else if (*s == L'"') {
+ len += slash + 2u;
+ slash = 0;
+ } else {
+ slash = 0;
+ ++len;
+ }
+ }
+ return len + slash;
+}
+
+static wchar_t* db_arg_quote_into(wchar_t* out, const wchar_t* s) {
+ size_t slash = 0;
+ int quote = db_arg_needs_quote(s);
+ if (!quote) {
+ while (*s) *out++ = *s++;
+ return out;
+ }
+ *out++ = L'"';
+ for (; *s; ++s) {
+ if (*s == L'\\') {
+ *out++ = *s;
+ ++slash;
+ } else if (*s == L'"') {
+ while (slash--) *out++ = L'\\';
+ *out++ = L'\\';
+ *out++ = L'"';
+ slash = 0;
+ } else {
+ *out++ = *s;
+ slash = 0;
+ }
+ }
+ while (slash--) *out++ = L'\\';
+ *out++ = L'"';
+ return out;
+}
+
+static wchar_t* db_command_line(wchar_t** argv, size_t argc) {
+ size_t i, len = 1u;
+ wchar_t* out;
+ wchar_t* p;
+ for (i = 0; i < argc; ++i)
+ len += db_arg_quoted_len(argv[i]) + (i ? 1u : 0u);
+ out = (wchar_t*)malloc(len * sizeof(*out));
+ if (!out) return NULL;
+ p = out;
+ for (i = 0; i < argc; ++i) {
+ if (i) *p++ = L' ';
+ p = db_arg_quote_into(p, argv[i]);
+ }
+ *p = L'\0';
+ return out;
+}
+
+static int db_wenv_cmp(const void* a, const void* b) {
+ const wchar_t* const* aa = (const wchar_t* const*)a;
+ const wchar_t* const* bb = (const wchar_t* const*)b;
+ return lstrcmpiW(*aa, *bb);
+}
+
+static wchar_t* db_env_block(wchar_t** envv, size_t nenv) {
+ size_t i, total = 2u, off = 0;
+ wchar_t* block;
+ qsort(envv, nenv, sizeof(*envv), db_wenv_cmp);
+ for (i = 0; i < nenv; ++i) total += (size_t)lstrlenW(envv[i]) + 1u;
+ block = (wchar_t*)malloc(total * sizeof(*block));
+ if (!block) return NULL;
+ for (i = 0; i < nenv; ++i) {
+ size_t n = (size_t)lstrlenW(envv[i]) + 1u;
+ memcpy(block + off, envv[i], n * sizeof(*block));
+ off += n;
+ }
+ block[off++] = L'\0';
+ block[off] = L'\0';
+ return block;
+}
+
+static int db_exec_spawn_common(void* user, const KitSlice* argv, size_t argc,
+ const KitBuildKV* envv, size_t nenv,
+ KitSlice cwd, KitSlice stdout_path,
+ KitSlice stderr_path, int capture,
+ KitBuildProc** out) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ char** av = NULL;
+ char** ev = NULL;
+ wchar_t** wav = NULL;
+ wchar_t** wev = NULL;
+ wchar_t* cmdline = NULL;
+ wchar_t* envblock = NULL;
+ char* cwd_s = NULL;
+ wchar_t* wcwd = NULL;
+ KitBuildProc* proc = NULL;
+ HANDLE stdout_h = INVALID_HANDLE_VALUE;
+ HANDLE stderr_h = INVALID_HANDLE_VALUE;
+ HANDLE stdin_h = INVALID_HANDLE_VALUE;
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES sa;
+ DWORD flags = CREATE_UNICODE_ENVIRONMENT;
+ size_t i;
+ if (!bh || !bh->env || !argv || argc == 0u || !out) return 1;
+ *out = NULL;
+ av = (char**)driver_alloc(bh->env, (argc + 1u) * sizeof *av);
+ ev = (char**)driver_alloc(bh->env, (nenv + 1u) * sizeof *ev);
+ if (!av || !ev) goto err;
+ memset(av, 0, (argc + 1u) * sizeof *av);
+ memset(ev, 0, (nenv + 1u) * sizeof *ev);
+ for (i = 0; i < argc; ++i) {
+ av[i] = i == 0u ? db_cwd_join(bh->env, argv[i])
+ : db_slice_dup(bh->env, argv[i]);
+ if (!av[i]) goto err;
+ }
+ for (i = 0; i < nenv; ++i) {
+ size_t nk = envv[i].key.len, nv = envv[i].value.len;
+ ev[i] = (char*)driver_alloc(bh->env, nk + 1u + nv + 1u);
+ if (!ev[i]) goto err;
+ memcpy(ev[i], envv[i].key.s, nk);
+ ev[i][nk] = '=';
+ memcpy(ev[i] + nk + 1u, envv[i].value.s, nv);
+ ev[i][nk + 1u + nv] = '\0';
+ }
+ wav = db_widen_strv(av, argc);
+ wev = db_widen_strv(ev, nenv);
+ if (!wav || !wev) goto err;
+ cmdline = db_command_line(wav, argc);
+ envblock = db_env_block(wev, nenv);
+ cwd_s = db_slice_dup(bh->env, cwd);
+ wcwd = db_widen(cwd_s);
+ proc = (KitBuildProc*)driver_alloc_zeroed(bh->env, sizeof *proc);
+ if (!cmdline || !envblock || !cwd_s || !wcwd || !proc) goto err;
+
+ memset(&si, 0, sizeof si);
+ memset(&pi, 0, sizeof pi);
+ si.cb = sizeof si;
+ if (capture) {
+ char* stdout_s = db_slice_dup(bh->env, stdout_path);
+ char* stderr_s = db_slice_dup(bh->env, stderr_path);
+ wchar_t* wout = stdout_s ? db_widen(stdout_s) : NULL;
+ wchar_t* werr = stderr_s ? db_widen(stderr_s) : NULL;
+ wchar_t* wnull = db_widen("NUL");
+ if (stdout_s) driver_free(bh->env, stdout_s, strlen(stdout_s) + 1u);
+ if (stderr_s) driver_free(bh->env, stderr_s, strlen(stderr_s) + 1u);
+ if (!wout || !werr || !wnull) {
+ free(wout);
+ free(werr);
+ free(wnull);
+ goto err;
+ }
+ memset(&sa, 0, sizeof sa);
+ sa.nLength = sizeof sa;
+ sa.bInheritHandle = TRUE;
+ stdout_h = CreateFileW(wout, GENERIC_WRITE, FILE_SHARE_READ, &sa,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ stderr_h = CreateFileW(werr, GENERIC_WRITE, FILE_SHARE_READ, &sa,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ stdin_h = CreateFileW(wnull, GENERIC_READ, FILE_SHARE_READ, &sa,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ free(wout);
+ free(werr);
+ free(wnull);
+ if (stdout_h == INVALID_HANDLE_VALUE || stderr_h == INVALID_HANDLE_VALUE ||
+ stdin_h == INVALID_HANDLE_VALUE)
+ goto err;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = stdin_h;
+ si.hStdOutput = stdout_h;
+ si.hStdError = stderr_h;
+ }
+ if (!CreateProcessW(wav[0], cmdline, NULL, NULL, capture ? TRUE : FALSE,
+ flags, envblock, wcwd, &si, &pi))
+ goto err;
+ CloseHandle(pi.hThread);
+ if (stdout_h != INVALID_HANDLE_VALUE) CloseHandle(stdout_h);
+ if (stderr_h != INVALID_HANDLE_VALUE) CloseHandle(stderr_h);
+ if (stdin_h != INVALID_HANDLE_VALUE) CloseHandle(stdin_h);
+ proc->process = pi.hProcess;
+ *out = proc;
+ db_free_wstrv(wav, argc);
+ db_free_wstrv(wev, nenv);
+ db_free_strv(bh->env, av, argc);
+ db_free_strv(bh->env, ev, nenv);
+ driver_free(bh->env, cwd_s, strlen(cwd_s) + 1u);
+ free(wcwd);
+ free(cmdline);
+ free(envblock);
+ return 0;
+
+err:
+ if (stdout_h != INVALID_HANDLE_VALUE) CloseHandle(stdout_h);
+ if (stderr_h != INVALID_HANDLE_VALUE) CloseHandle(stderr_h);
+ if (stdin_h != INVALID_HANDLE_VALUE) CloseHandle(stdin_h);
+ if (proc) driver_free(bh->env, proc, sizeof *proc);
+ if (cwd_s) driver_free(bh->env, cwd_s, strlen(cwd_s) + 1u);
+ free(wcwd);
+ free(cmdline);
+ free(envblock);
+ db_free_wstrv(wav, argc);
+ db_free_wstrv(wev, nenv);
+ db_free_strv(bh->env, av, argc);
+ db_free_strv(bh->env, ev, nenv);
+ return 1;
+}
+
+static int db_exec_spawn(void* user, const KitSlice* argv, size_t argc,
+ const KitBuildKV* envv, size_t nenv, KitSlice cwd,
+ KitBuildProc** out) {
+ return db_exec_spawn_common(user, argv, argc, envv, nenv, cwd,
+ KIT_SLICE_NULL, KIT_SLICE_NULL, 0, out);
+}
+
+static int db_exec_spawn_capture(void* user, const KitSlice* argv, size_t argc,
+ const KitBuildKV* envv, size_t nenv,
+ KitSlice cwd, KitSlice stdout_path,
+ KitSlice stderr_path, KitBuildProc** out) {
+ return db_exec_spawn_common(user, argv, argc, envv, nenv, cwd, stdout_path,
+ stderr_path, 1, out);
+}
+
+static int db_proc_exit_code(HANDLE h, int* exit_code) {
+ DWORD code;
+ if (!GetExitCodeProcess(h, &code)) return 1;
+ *exit_code = code > 255u ? 128 : (int)code;
+ return 0;
+}
+
+static int db_exec_wait(void* user, KitBuildProc* proc, int* exit_code) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ if (!bh || !proc || !exit_code) return 1;
+ if (WaitForSingleObject(proc->process, INFINITE) != WAIT_OBJECT_0)
+ return 1;
+ if (db_proc_exit_code(proc->process, exit_code) != 0) return 1;
+ CloseHandle(proc->process);
+ driver_free(bh->env, proc, sizeof *proc);
+ return 0;
+}
+
+static int db_exec_poll(void* user, KitBuildProc* proc, int* done,
+ int* exit_code) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ DWORD r;
+ if (!bh || !proc || !done || !exit_code) return 1;
+ *done = 0;
+ r = WaitForSingleObject(proc->process, 0);
+ if (r == WAIT_TIMEOUT) return 0;
+ if (r != WAIT_OBJECT_0) return 1;
+ if (db_proc_exit_code(proc->process, exit_code) != 0) return 1;
+ CloseHandle(proc->process);
+ driver_free(bh->env, proc, sizeof *proc);
+ *done = 1;
+ return 0;
+}
+
+static void db_exec_kill(void* user, KitBuildProc* proc) {
+ (void)user;
+ if (proc) TerminateProcess(proc->process, 128u);
+}
+
+static int db_pipe_start(KitBuildListener* l) {
+ wchar_t* wname;
+ BOOL ok;
+ DWORD err;
+ if (!l || l->pipe != INVALID_HANDLE_VALUE) return 1;
+ wname = db_widen(l->name);
+ if (!wname) return 1;
+ l->event = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (!l->event) {
+ free(wname);
+ return 1;
+ }
+ memset(&l->ov, 0, sizeof l->ov);
+ l->ov.hEvent = l->event;
+ l->connected = 0;
+ l->pipe = CreateNamedPipeW(
+ wname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
+ DRIVER_BUILD_FRAME_MAX + 4u, DRIVER_BUILD_FRAME_MAX + 4u, 0, NULL);
+ free(wname);
+ if (l->pipe == INVALID_HANDLE_VALUE) {
+ CloseHandle(l->event);
+ l->event = NULL;
+ return 1;
+ }
+ ok = ConnectNamedPipe(l->pipe, &l->ov);
+ err = ok ? 0 : GetLastError();
+ if (ok) {
+ l->connected = 1;
+ SetEvent(l->event);
+ return 0;
+ }
+ if (err == ERROR_IO_PENDING) return 0;
+ if (err == ERROR_PIPE_CONNECTED) {
+ l->connected = 1;
+ SetEvent(l->event);
+ return 0;
+ }
+ CloseHandle(l->pipe);
+ CloseHandle(l->event);
+ l->pipe = INVALID_HANDLE_VALUE;
+ l->event = NULL;
+ return 1;
+}
+
+static int db_pipe_finish(DriverBuildHost* bh, KitBuildListener* l,
+ DWORD timeout, KitBuildConn** out, int* ready) {
+ DWORD r;
+ DWORD got = 0;
+ KitBuildConn* c;
+ if (!bh || !l || !out || !ready) return 1;
+ *out = NULL;
+ *ready = 0;
+ if (l->pipe == INVALID_HANDLE_VALUE && db_pipe_start(l) != 0) return 1;
+ r = WaitForSingleObject(l->event, timeout);
+ if (r == WAIT_TIMEOUT) return 0;
+ if (r != WAIT_OBJECT_0) return 1;
+ if (!l->connected &&
+ !GetOverlappedResult(l->pipe, &l->ov, &got, FALSE))
+ return 1;
+ c = (KitBuildConn*)driver_alloc_zeroed(bh->env, sizeof *c);
+ if (!c) return 1;
+ c->h = l->pipe;
+ l->pipe = INVALID_HANDLE_VALUE;
+ if (l->event) CloseHandle(l->event);
+ l->event = NULL;
+ memset(&l->ov, 0, sizeof l->ov);
+ l->connected = 0;
+ *out = c;
+ *ready = 1;
+ return 0;
+}
+
+static int db_transport_listen(void* user, char* name_out, size_t cap,
+ KitBuildListener** out) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ KitBuildListener* l;
+ int n;
+ if (!bh || !bh->env || !name_out || cap == 0u || !out) return 1;
+ *out = NULL;
+ l = (KitBuildListener*)driver_alloc_zeroed(bh->env, sizeof *l);
+ if (!l) return 1;
+ l->pipe = INVALID_HANDLE_VALUE;
+ n = snprintf(l->name, sizeof l->name, "\\\\.\\pipe\\kit-build-%lu-%llu",
+ (unsigned long)GetCurrentProcessId(),
+ (unsigned long long)bh->next_endpoint++);
+ if (n < 0 || (size_t)n >= sizeof l->name || (size_t)n + 1u > cap)
+ goto err;
+ if (db_pipe_start(l) != 0) goto err;
+ memcpy(name_out, l->name, (size_t)n + 1u);
+ *out = l;
+ return 0;
+
+err:
+ if (l->pipe != INVALID_HANDLE_VALUE) CloseHandle(l->pipe);
+ if (l->event) CloseHandle(l->event);
+ driver_free(bh->env, l, sizeof *l);
+ return 1;
+}
+
+static int db_transport_accept(void* user, KitBuildListener* lst,
+ KitBuildConn** out) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ int ready = 0;
+ if (db_pipe_finish(bh, lst, INFINITE, out, &ready) != 0) return 1;
+ return ready ? 0 : 1;
+}
+
+static int db_transport_try_accept(void* user, KitBuildListener* lst,
+ KitBuildConn** out, int* ready) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ return db_pipe_finish(bh, lst, DRIVER_BUILD_PIPE_TIMEOUT_MS, out, ready);
+}
+
+static void db_transport_close_listener(void* user, KitBuildListener* lst) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ if (!bh || !lst) return;
+ if (lst->pipe != INVALID_HANDLE_VALUE) {
+ CancelIo(lst->pipe);
+ CloseHandle(lst->pipe);
+ }
+ if (lst->event) CloseHandle(lst->event);
+ driver_free(bh->env, lst, sizeof *lst);
+}
+
+static int db_transport_dial(void* user, KitSlice endpoint, KitBuildConn** out) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ char name[KIT_BUILD_PATH_MAX];
+ wchar_t* wname;
+ HANDLE h = INVALID_HANDLE_VALUE;
+ KitBuildConn* c;
+ DWORD i;
+ if (!bh || !bh->env || !out || db_path_copy(name, sizeof name, endpoint))
+ return 1;
+ *out = NULL;
+ wname = db_widen(name);
+ if (!wname) return 1;
+ for (i = 0; i < 500u; ++i) {
+ h = CreateFileW(wname, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (h != INVALID_HANDLE_VALUE) break;
+ if (GetLastError() == ERROR_PIPE_BUSY) {
+ WaitNamedPipeW(wname, 50u);
+ } else if (GetLastError() == ERROR_FILE_NOT_FOUND ||
+ GetLastError() == ERROR_PATH_NOT_FOUND) {
+ Sleep(10u);
+ } else {
+ free(wname);
+ return 1;
+ }
+ }
+ free(wname);
+ if (h == INVALID_HANDLE_VALUE) return 1;
+ c = (KitBuildConn*)driver_alloc_zeroed(bh->env, sizeof *c);
+ if (!c) {
+ CloseHandle(h);
+ return 1;
+ }
+ c->h = h;
+ *out = c;
+ return 0;
+}
+
+static int db_pipe_transfer(HANDLE h, int write, uint8_t* buf, size_t n) {
+ size_t off = 0;
+ while (off < n) {
+ DWORD chunk = n - off > 0x40000000u ? 0x40000000u : (DWORD)(n - off);
+ DWORD done = 0;
+ OVERLAPPED ov;
+ HANDLE ev;
+ BOOL ok;
+ ev = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (!ev) return 1;
+ memset(&ov, 0, sizeof ov);
+ ov.hEvent = ev;
+ ok = write ? WriteFile(h, buf + off, chunk, &done, &ov)
+ : ReadFile(h, buf + off, chunk, &done, &ov);
+ if (!ok && GetLastError() == ERROR_IO_PENDING)
+ ok = GetOverlappedResult(h, &ov, &done, TRUE);
+ CloseHandle(ev);
+ if (!ok || done == 0u) return 1;
+ off += done;
+ }
+ return 0;
+}
+
+static int db_transport_read(void* user, KitBuildConn* conn, uint8_t* buf,
+ size_t cap, size_t* n) {
+ uint8_t hdr[4];
+ uint32_t len;
+ (void)user;
+ if (!conn || !buf || !n) return 1;
+ if (db_pipe_transfer(conn->h, 0, hdr, sizeof hdr) != 0) return 1;
+ len = (uint32_t)hdr[0] | ((uint32_t)hdr[1] << 8) |
+ ((uint32_t)hdr[2] << 16) | ((uint32_t)hdr[3] << 24);
+ if (len > cap || len > DRIVER_BUILD_FRAME_MAX) return 1;
+ if (len && db_pipe_transfer(conn->h, 0, buf, len) != 0) return 1;
+ *n = len;
+ return 0;
+}
+
+static int db_transport_write(void* user, KitBuildConn* conn,
+ const uint8_t* buf, size_t n) {
+ uint8_t hdr[4];
+ (void)user;
+ if (!conn || (!buf && n) || n > DRIVER_BUILD_FRAME_MAX) return 1;
+ hdr[0] = (uint8_t)(n & 0xffu);
+ hdr[1] = (uint8_t)((n >> 8) & 0xffu);
+ hdr[2] = (uint8_t)((n >> 16) & 0xffu);
+ hdr[3] = (uint8_t)((n >> 24) & 0xffu);
+ return db_pipe_transfer(conn->h, 1, hdr, sizeof hdr) ||
+ (n ? db_pipe_transfer(conn->h, 1, (uint8_t*)buf, n) : 0);
+}
+
+static void db_transport_close(void* user, KitBuildConn* conn) {
+ DriverBuildHost* bh = (DriverBuildHost*)user;
+ if (!bh || !conn) return;
+ if (conn->h != INVALID_HANDLE_VALUE) {
+ DisconnectNamedPipe(conn->h);
+ CloseHandle(conn->h);
+ }
+ driver_free(bh->env, conn, sizeof *conn);
+}
+
+int driver_build_host_init(DriverBuildHost* out, DriverEnv* env) {
+ if (!out || !env) return 1;
+ memset(out, 0, sizeof *out);
+ out->env = env;
+ out->cas_host = driver_cas_host(env);
+ out->store_io.rename = db_store_rename;
+ out->store_io.remove = db_store_remove;
+ out->store_io.make_temp_dir = db_store_make_temp_dir;
+ out->store_io.sync_path = db_store_sync_path;
+ out->store_io.list_dir = db_store_list_dir;
+ out->store_io.user = out;
+ out->exec.spawn = db_exec_spawn;
+ out->exec.spawn_capture = db_exec_spawn_capture;
+ out->exec.wait = db_exec_wait;
+ out->exec.poll = db_exec_poll;
+ out->exec.kill = db_exec_kill;
+ out->exec.user = out;
+ out->transport.listen = db_transport_listen;
+ out->transport.accept = db_transport_accept;
+ out->transport.try_accept = db_transport_try_accept;
+ out->transport.close_listener = db_transport_close_listener;
+ out->transport.dial = db_transport_dial;
+ out->transport.read_frame = db_transport_read;
+ out->transport.write_frame = db_transport_write;
+ out->transport.close = db_transport_close;
+ out->transport.user = out;
+ out->host.cas_host = &out->cas_host;
+ out->host.store_io = &out->store_io;
+ out->host.exec = &out->exec;
+ out->host.transport = &out->transport;
+ out->next_endpoint = 1;
+ return 0;
+}
+
+void driver_build_host_fini(DriverBuildHost* host) { (void)host; }
+
+char* driver_build_host_abs_path(DriverEnv* env, const char* path,
+ size_t* out_size) {
+ wchar_t* wpath;
+ wchar_t* wbuf;
+ char* narrowed;
+ char* out;
+ DWORD need;
+ size_t size;
+ if (!env || !path) return NULL;
+ wpath = db_widen(path);
+ if (!wpath) return NULL;
+ need = GetFullPathNameW(wpath, 0, NULL, NULL);
+ if (need == 0) {
+ free(wpath);
+ return NULL;
+ }
+ wbuf = (wchar_t*)malloc((size_t)need * sizeof(*wbuf));
+ if (!wbuf || GetFullPathNameW(wpath, need, wbuf, NULL) == 0) {
+ free(wbuf);
+ free(wpath);
+ return NULL;
+ }
+ free(wpath);
+ narrowed = db_narrow(wbuf);
+ free(wbuf);
+ if (!narrowed) return NULL;
+ size = strlen(narrowed) + 1u;
+ out = (char*)driver_alloc(env, size);
+ if (out) {
+ memcpy(out, narrowed, size);
+ if (out_size) *out_size = size;
+ }
+ free(narrowed);
+ return out;
+}
diff --git a/mk/driver_srcs.mk b/mk/driver_srcs.mk
@@ -87,7 +87,7 @@ DRIVER_SRCS += $(call need-any,RUN,driver/lib/wasm_run.c)
DRIVER_SRCS += $(call need-any,CC CHECK BUILD_EXE BUILD_LIB BUILD_OBJ,driver/lib/link_inputs.c)
DRIVER_SRCS += $(call need-any,CAS PKG BUILD UPDATE,driver/lib/dist_host.c)
ifeq ($(HOST_OS),windows)
-DRIVER_SRCS += $(call need-any,BUILD,driver/env/build_host_stub.c)
+DRIVER_SRCS += $(call need-any,BUILD,driver/env/build_host_windows.c)
else
DRIVER_SRCS += $(call need-any,BUILD,driver/env/build_host_posix.c)
endif