kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Adriver/env/build_host_windows.c | 862+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmk/driver_srcs.mk | 2+-
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