diff --git a/build.zig b/build.zig index 6a9d2051e3..a127e76c92 100644 --- a/build.zig +++ b/build.zig @@ -4,7 +4,7 @@ const builtin = @import("builtin"); const Build = std.Build; const Step = Build.Step; const Compile = Step.Compile; -const LazyPath = Step.LazyPath; +const LazyPath = Build.LazyPath; const Target = std.Target; const ResolvedTarget = std.Build.ResolvedTarget; const CrossTarget = std.zig.CrossTarget; @@ -388,7 +388,22 @@ pub fn build(b: *Build) !void { // zig build translate-c-headers { const step = b.step("translate-c", "Copy generated translated-c-headers.zig to zig-out"); - step.dependOn(&b.addInstallFile(getTranslateC(b, b.graph.host, .Debug).getOutput(), "translated-c-headers.zig").step); + for ([_]TargetDescription{ + .{ .os = .windows, .arch = .x86_64 }, + .{ .os = .mac, .arch = .x86_64 }, + .{ .os = .mac, .arch = .aarch64 }, + .{ .os = .linux, .arch = .x86_64 }, + .{ .os = .linux, .arch = .aarch64 }, + .{ .os = .linux, .arch = .x86_64, .musl = true }, + .{ .os = .linux, .arch = .aarch64, .musl = true }, + }) |t| { + const resolved = t.resolveTarget(b); + step.dependOn( + &b.addInstallFile(getTranslateC(b, resolved, .Debug), b.fmt("translated-c-headers/{s}.zig", .{ + resolved.result.zigTriple(b.allocator) catch @panic("OOM"), + })).step, + ); + } } // zig build enum-extractor @@ -405,23 +420,32 @@ pub fn build(b: *Build) !void { } } -pub fn addMultiCheck( +const TargetDescription = struct { + os: OperatingSystem, + arch: Arch, + musl: bool = false, + + fn resolveTarget(desc: TargetDescription, b: *Build) std.Build.ResolvedTarget { + return b.resolveTargetQuery(.{ + .os_tag = OperatingSystem.stdOSTag(desc.os), + .cpu_arch = desc.arch, + .cpu_model = getCpuModel(desc.os, desc.arch) orelse .determined_by_arch_os, + .os_version_min = getOSVersionMin(desc.os), + .glibc_version = if (desc.musl) null else getOSGlibCVersion(desc.os), + }); + } +}; + +fn addMultiCheck( b: *Build, parent_step: *Step, root_build_options: BunBuildOptions, - to_check: []const struct { os: OperatingSystem, arch: Arch, musl: bool = false }, + to_check: []const TargetDescription, optimize: []const std.builtin.OptimizeMode, ) void { for (to_check) |check| { for (optimize) |mode| { - const check_target = b.resolveTargetQuery(.{ - .os_tag = OperatingSystem.stdOSTag(check.os), - .cpu_arch = check.arch, - .cpu_model = getCpuModel(check.os, check.arch) orelse .determined_by_arch_os, - .os_version_min = getOSVersionMin(check.os), - .glibc_version = if (check.musl) null else getOSGlibCVersion(check.os), - }); - + const check_target = check.resolveTarget(b); var options: BunBuildOptions = .{ .target = check_target, .os = check.os, @@ -445,7 +469,7 @@ pub fn addMultiCheck( } } -fn getTranslateC(b: *Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *Step.TranslateC { +fn getTranslateC(b: *Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) LazyPath { const translate_c = b.addTranslateC(.{ .root_source_file = b.path("src/c-headers-for-zig.h"), .target = target, @@ -461,7 +485,35 @@ fn getTranslateC(b: *Build, target: std.Build.ResolvedTarget, optimize: std.buil const str, const value = entry; translate_c.defineCMacroRaw(b.fmt("{s}={d}", .{ str, @intFromBool(value) })); } - return translate_c; + + if (target.result.os.tag == .windows) { + // translate-c is unable to translate the unsuffixed windows functions + // like `SetCurrentDirectory` since they are defined with an odd macro + // that translate-c doesn't handle. + // + // #define SetCurrentDirectory __MINGW_NAME_AW(SetCurrentDirectory) + // + // In these cases, it's better to just reference the underlying function + // directly: SetCurrentDirectoryW. To make the error better, a post + // processing step is applied to the translate-c file. + // + // Additionally, this step makes it so that decls like NTSTATUS and + // HANDLE point to the standard library structures. + const helper_exe = b.addExecutable(.{ + .name = "process_windows_translate_c", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/codegen/process_windows_translate_c.zig"), + .target = b.graph.host, + .optimize = .Debug, + }), + }); + const in = translate_c.getOutput(); + const run = b.addRunArtifact(helper_exe); + run.addFileArg(in); + const out = run.addOutputFileArg("c-headers-for-zig.zig"); + return out; + } + return translate_c.getOutput(); } pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile { @@ -526,7 +578,7 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void { obj.root_module.addImport("build_options", opts.buildOptionsModule(b)); const translate_c = getTranslateC(b, opts.target, opts.optimize); - obj.root_module.addImport("translated-c-headers", translate_c.createModule()); + obj.root_module.addImport("translated-c-headers", b.createModule(.{ .root_source_file = translate_c })); } const ObjectFormat = enum { diff --git a/misctools/lldb/lldb_pretty_printers.py b/misctools/lldb/lldb_pretty_printers.py index 0ed2e6667e..4b0993ca43 100644 --- a/misctools/lldb/lldb_pretty_printers.py +++ b/misctools/lldb/lldb_pretty_printers.py @@ -61,7 +61,6 @@ zig_keywords = { 'try', 'union', 'unreachable', - 'usingnamespace', 'var', 'volatile', 'while', diff --git a/src/brotli.zig b/src/brotli.zig index 8cd6d0eba6..908d1ead25 100644 --- a/src/brotli.zig +++ b/src/brotli.zig @@ -1,9 +1,6 @@ const bun = @import("root").bun; const std = @import("std"); -pub const c = struct { - pub usingnamespace @import("./deps/brotli_decoder.zig"); - pub usingnamespace @import("./deps/brotli_encoder.zig"); -}; +pub const c = @import("./deps/brotli_c.zig"); const BrotliDecoder = c.BrotliDecoder; const BrotliEncoder = c.BrotliEncoder; diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index 0b868bafcf..d561772d08 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -2706,10 +2706,10 @@ const LifecycleScriptSubprocess = bun.install.LifecycleScriptSubprocess; const Body = JSC.WebCore.Body; const IPClog = Output.scoped(.IPC, false); -const PosixSpawn = bun.posix.spawn; -const Rusage = bun.posix.spawn.Rusage; -const Process = bun.posix.spawn.Process; -const WaiterThread = bun.posix.spawn.WaiterThread; +const PosixSpawn = bun.spawn; +const Rusage = bun.spawn.Rusage; +const Process = bun.spawn.Process; +const WaiterThread = bun.spawn.WaiterThread; const Stdio = bun.spawn.Stdio; const StdioResult = if (Environment.isWindows) bun.spawn.WindowsSpawnResult.StdioResult else ?bun.FileDescriptor; diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 89fefe4495..80f6041225 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -373,7 +373,7 @@ pub fn onJSCInvalidEnvVar(name: [*]const u8, len: usize) callconv(.C) void { /// broke when I just used it. Not sure. ... but this works! fn @"windows process.dlopen"(str: *bun.String) callconv(.C) ?*anyopaque { if (comptime !bun.Environment.isWindows) { - unreachable; + @compileError(unreachable); } var buf: bun.WPathBuffer = undefined; @@ -390,7 +390,7 @@ fn @"windows process.dlopen"(str: *bun.String) callconv(.C) ?*anyopaque { }; buf[data.len] = 0; const LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008; - return bun.windows.LoadLibraryExW(buf[0..data.len :0].ptr, null, LOAD_WITH_ALTERED_SEARCH_PATH); + return bun.windows.kernel32.LoadLibraryExW(buf[0..data.len :0].ptr, null, LOAD_WITH_ALTERED_SEARCH_PATH); } comptime { diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index ec4bd29970..d4ee20eb31 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -5,6 +5,7 @@ const std = @import("std"); const bun = @import("root").bun; const strings = bun.strings; const windows = bun.windows; +const c = bun.c; const string = bun.string; const JSC = bun.JSC; const PathString = JSC.PathString; @@ -761,8 +762,8 @@ pub fn NewAsyncCpTask(comptime is_shell: bool) type { const dest = args.dest.osPath(&dest_buf); if (Environment.isWindows) { - const attributes = windows.GetFileAttributesW(src); - if (attributes == windows.INVALID_FILE_ATTRIBUTES) { + const attributes = c.GetFileAttributesW(src); + if (attributes == c.INVALID_FILE_ATTRIBUTES) { this.finishConcurrently(.{ .err = .{ .errno = @intFromEnum(C.SystemErrno.ENOENT), .syscall = .copyfile, @@ -770,7 +771,7 @@ pub fn NewAsyncCpTask(comptime is_shell: bool) type { } }); return; } - const file_or_symlink = (attributes & windows.FILE_ATTRIBUTE_DIRECTORY) == 0 or (attributes & windows.FILE_ATTRIBUTE_REPARSE_POINT) != 0; + const file_or_symlink = (attributes & c.FILE_ATTRIBUTE_DIRECTORY) == 0 or (attributes & c.FILE_ATTRIBUTE_REPARSE_POINT) != 0; if (file_or_symlink) { const r = nodefs._copySingleFileSync( src, @@ -3990,15 +3991,6 @@ pub const NodeFS = struct { mode: Mode, comptime return_path: bool, ) Maybe(Return.Mkdir) { - const callbacks = struct { - pub fn onCreateDir(c: Ctx, dirpath: bun.OSPathSliceZ) void { - if (Ctx != void) { - c.onCreateDir(dirpath); - } - return; - } - }; - const Char = bun.OSPathChar; const len: u16 = @truncate(path.len); @@ -4042,7 +4034,7 @@ pub const NodeFS = struct { } }, .result => { - callbacks.onCreateDir(ctx, path); + if (Ctx != void) ctx.onCreateDir(path); if (!return_path) { return .{ .result = .{ .none = {} } }; } @@ -4084,7 +4076,7 @@ pub const NodeFS = struct { } }, .result => { - callbacks.onCreateDir(ctx, parent); + if (Ctx != void) ctx.onCreateDir(parent); // We found a parent that worked working_mem[i] = std.fs.path.sep; break; @@ -4115,7 +4107,7 @@ pub const NodeFS = struct { }, .result => { - callbacks.onCreateDir(ctx, parent); + if (Ctx != void) ctx.onCreateDir(parent); working_mem[i] = std.fs.path.sep; }, } @@ -4141,7 +4133,7 @@ pub const NodeFS = struct { .result => {}, } - callbacks.onCreateDir(ctx, working_mem[0..len :0]); + if (Ctx != void) ctx.onCreateDir(working_mem[0..len :0]); if (!return_path) { return .{ .result = .{ .none = {} } }; } @@ -6048,8 +6040,8 @@ pub const NodeFS = struct { const dest = dest_buf[0..dest_dir_len :0]; if (Environment.isWindows) { - const attributes = windows.GetFileAttributesW(src); - if (attributes == windows.INVALID_FILE_ATTRIBUTES) { + const attributes = bun.c.GetFileAttributesW(src); + if (attributes == bun.c.INVALID_FILE_ATTRIBUTES) { return .{ .err = .{ .errno = @intFromEnum(C.SystemErrno.ENOENT), .syscall = .copyfile, @@ -6057,7 +6049,7 @@ pub const NodeFS = struct { } }; } - if ((attributes & windows.FILE_ATTRIBUTE_DIRECTORY) == 0) { + if ((attributes & bun.c.FILE_ATTRIBUTE_DIRECTORY) == 0) { const r = this._copySingleFileSync( src, dest, @@ -6509,12 +6501,12 @@ pub const NodeFS = struct { if (Environment.isWindows) { const src_enoent_maybe = ret.initErrWithP(.ENOENT, .copyfile, this.osPathIntoSyncErrorBuf(src)); const dst_enoent_maybe = ret.initErrWithP(.ENOENT, .copyfile, this.osPathIntoSyncErrorBuf(dest)); - const stat_ = reuse_stat orelse switch (windows.GetFileAttributesW(src)) { - windows.INVALID_FILE_ATTRIBUTES => return ret.errnoSysP(0, .copyfile, this.osPathIntoSyncErrorBuf(src)).?, + const stat_ = reuse_stat orelse switch (bun.c.GetFileAttributesW(src)) { + bun.c.INVALID_FILE_ATTRIBUTES => return ret.errnoSysP(0, .copyfile, this.osPathIntoSyncErrorBuf(src)).?, else => |result| result, }; - if (stat_ & windows.FILE_ATTRIBUTE_REPARSE_POINT == 0) { - if (windows.CopyFileW(src, dest, @intFromBool(mode.shouldntOverwrite())) == 0) { + if (stat_ & bun.c.FILE_ATTRIBUTE_REPARSE_POINT == 0) { + if (bun.c.CopyFileW(src, dest, @intFromBool(mode.shouldntOverwrite())) == 0) { var err = windows.GetLastError(); var errpath: bun.OSPathSliceZ = undefined; switch (err) { diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index cec29acf90..5749df4b0e 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1928,7 +1928,7 @@ pub const Process = struct { buf2[len2] = 0; break :str buf2[0..len2 :0].ptr; } else null; - _ = bun.windows.SetEnvironmentVariableW(buf1[0..len1 :0].ptr, str2); + _ = bun.c.SetEnvironmentVariableW(buf1[0..len1 :0].ptr, str2); } comptime { diff --git a/src/bun.js/webcore.zig b/src/bun.js/webcore.zig index c1d94eedb4..88f7c9d4db 100644 --- a/src/bun.js/webcore.zig +++ b/src/bun.js/webcore.zig @@ -16,6 +16,7 @@ pub const AbortSignal = @import("./bindings/bindings.zig").AbortSignal; pub const JSValue = @import("./bindings/bindings.zig").JSValue; const Environment = bun.Environment; const UUID7 = @import("./uuid.zig").UUID7; +const c = bun.c; pub const Lifetime = enum { clone, @@ -268,11 +269,11 @@ pub const Prompt = struct { // unset `ENABLE_VIRTUAL_TERMINAL_INPUT` on windows. This prevents backspace from // deleting the entire line const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; + bun.windows.updateStdioModeFlags(.std_in, .{ .unset = c.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; defer if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode(bun.FD.stdin().native(), mode); + _ = bun.c.SetConsoleMode(bun.FD.stdin().native(), mode); } }; diff --git a/src/bun.zig b/src/bun.zig index 4cfe9e1f31..8d8d2c270a 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -1547,7 +1547,7 @@ pub fn reloadProcess( if (comptime Environment.isWindows) { // on windows we assume that we have a parent process that is monitoring us and will restart us if we exit with a magic exit code // see becomeWatcherManager - const rc = bun.windows.TerminateProcess(bun.windows.GetCurrentProcess(), win32.watcher_reload_exit); + const rc = c.TerminateProcess(c.GetCurrentProcess(), windows.watcher_reload_exit); if (rc == 0) { const err = bun.windows.GetLastError(); if (may_return) { @@ -1564,7 +1564,6 @@ pub fn reloadProcess( } } - const PosixSpawn = posix.spawn; const dupe_argv = allocator.allocSentinel(?[*:0]const u8, bun.argv.len, null) catch unreachable; for (bun.argv, dupe_argv) |src, *dest| { dest.* = (allocator.dupeZ(u8, src) catch unreachable).ptr; @@ -1591,24 +1590,24 @@ pub fn reloadProcess( // macOS doesn't have CLOEXEC, so we must go through posix_spawn if (comptime Environment.isMac) { - var actions = PosixSpawn.Actions.init() catch unreachable; + var actions = spawn.Actions.init() catch unreachable; actions.inherit(.stdin()) catch unreachable; actions.inherit(.stdout()) catch unreachable; actions.inherit(.stderr()) catch unreachable; - var attrs = PosixSpawn.Attr.init() catch unreachable; + var attrs = spawn.Attr.init() catch unreachable; attrs.resetSignals() catch {}; attrs.set( - C.translated.POSIX_SPAWN_CLOEXEC_DEFAULT | + c.POSIX_SPAWN_CLOEXEC_DEFAULT | // Apple Extension: If this bit is set, rather // than returning to the caller, posix_spawn(2) // and posix_spawnp(2) will behave as a more // featureful execve(2). - C.translated.POSIX_SPAWN_SETEXEC | - C.translated.POSIX_SPAWN_SETSIGDEF | C.translated.POSIX_SPAWN_SETSIGMASK, + c.POSIX_SPAWN_SETEXEC | + c.POSIX_SPAWN_SETSIGDEF | c.POSIX_SPAWN_SETSIGMASK, ) catch unreachable; - switch (PosixSpawn.spawnZ(exec_path, actions, attrs, @as([*:null]?[*:0]const u8, @ptrCast(newargv)), @as([*:null]?[*:0]const u8, @ptrCast(envp)))) { + switch (spawn.spawnZ(exec_path, actions, attrs, @as([*:null]?[*:0]const u8, @ptrCast(newargv)), @as([*:null]?[*:0]const u8, @ptrCast(envp)))) { .err => |err| { if (may_return) { Output.errGeneric("Failed to reload process: {s}", .{@tagName(err.getErrno())}); @@ -1931,8 +1930,8 @@ const WindowsStat = extern struct { pub const Stat = if (Environment.isWindows) windows.libuv.uv_stat_t else std.posix.Stat; pub const StatFS = switch (Environment.os) { - .mac => C.translated.struct_statfs, - .linux => C.translated.struct_statfs, + .mac => bun.c.struct_statfs, + .linux => bun.c.struct_statfs, else => windows.libuv.uv_statfs_t, }; @@ -1999,201 +1998,7 @@ pub fn initArgv(allocator: std.mem.Allocator) !void { } } -pub const posix = struct { - pub const spawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn; -}; - -pub const win32 = struct { - const w = std.os.windows; - - /// Returns the original mode, or null on failure - pub fn updateStdioModeFlags(i: bun.FD.Stdio, opts: struct { set: w.DWORD = 0, unset: w.DWORD = 0 }) !w.DWORD { - const fd = i.fd(); - var original_mode: w.DWORD = 0; - if (windows.GetConsoleMode(fd.cast(), &original_mode) != 0) { - if (windows.SetConsoleMode(fd.cast(), (original_mode | opts.set) & ~opts.unset) == 0) { - return windows.getLastError(); - } - } else return windows.getLastError(); - return original_mode; - } - - const watcherChildEnv: [:0]const u16 = strings.toUTF16Literal("_BUN_WATCHER_CHILD"); - // magic exit code to indicate to the watcher manager that the child process should be re-spawned - // this was randomly generated - we need to avoid using a common exit code that might be used by the script itself - const watcher_reload_exit: w.DWORD = 3224497970; - - pub const spawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn; - - pub fn isWatcherChild() bool { - var buf: [1]u16 = undefined; - return windows.GetEnvironmentVariableW(@constCast(watcherChildEnv.ptr), &buf, 1) > 0; - } - - pub fn becomeWatcherManager(allocator: std.mem.Allocator) noreturn { - // this process will be the parent of the child process that actually runs the script - var procinfo: std.os.windows.PROCESS_INFORMATION = undefined; - C.windows_enable_stdio_inheritance(); - const job = windows.CreateJobObjectA(null, null) orelse Output.panic( - "Could not create watcher Job Object: {s}", - .{@tagName(std.os.windows.kernel32.GetLastError())}, - ); - var jeli = std.mem.zeroes(windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION); - jeli.BasicLimitInformation.LimitFlags = - windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | - windows.JOB_OBJECT_LIMIT_BREAKAWAY_OK | - windows.JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | - windows.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; - if (windows.SetInformationJobObject( - job, - windows.JobObjectExtendedLimitInformation, - &jeli, - @sizeOf(windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION), - ) == 0) { - Output.panic( - "Could not configure watcher Job Object: {s}", - .{@tagName(std.os.windows.kernel32.GetLastError())}, - ); - } - - while (true) { - spawnWatcherChild(allocator, &procinfo, job) catch |err| { - handleErrorReturnTrace(err, @errorReturnTrace()); - if (err == error.Win32Error) { - Output.panic("Failed to spawn process: {s}\n", .{@tagName(std.os.windows.kernel32.GetLastError())}); - } - Output.panic("Failed to spawn process: {s}\n", .{@errorName(err)}); - }; - w.WaitForSingleObject(procinfo.hProcess, w.INFINITE) catch |err| { - Output.panic("Failed to wait for child process: {s}\n", .{@errorName(err)}); - }; - var exit_code: w.DWORD = 0; - if (w.kernel32.GetExitCodeProcess(procinfo.hProcess, &exit_code) == 0) { - const err = windows.GetLastError(); - _ = std.os.windows.ntdll.NtClose(procinfo.hProcess); - Output.panic("Failed to get exit code of child process: {s}\n", .{@tagName(err)}); - } - _ = std.os.windows.ntdll.NtClose(procinfo.hProcess); - - // magic exit code to indicate that the child process should be re-spawned - if (exit_code == watcher_reload_exit) { - continue; - } else { - Global.exit(exit_code); - } - } - } - - pub fn spawnWatcherChild( - allocator: std.mem.Allocator, - procinfo: *std.os.windows.PROCESS_INFORMATION, - job: w.HANDLE, - ) !void { - // https://devblogs.microsoft.com/oldnewthing/20230209-00/?p=107812 - var attr_size: usize = undefined; - _ = windows.InitializeProcThreadAttributeList(null, 1, 0, &attr_size); - const p = try allocator.alloc(u8, attr_size); - defer allocator.free(p); - if (windows.InitializeProcThreadAttributeList(p.ptr, 1, 0, &attr_size) == 0) { - return error.Win32Error; - } - if (windows.UpdateProcThreadAttribute( - p.ptr, - 0, - windows.PROC_THREAD_ATTRIBUTE_JOB_LIST, - @ptrCast(&job), - @sizeOf(w.HANDLE), - null, - null, - ) == 0) { - return error.Win32Error; - } - - const flags: std.os.windows.DWORD = w.CREATE_UNICODE_ENVIRONMENT | windows.EXTENDED_STARTUPINFO_PRESENT; - - const image_path = windows.exePathW(); - var wbuf: WPathBuffer = undefined; - @memcpy(wbuf[0..image_path.len], image_path); - wbuf[image_path.len] = 0; - - const image_pathZ = wbuf[0..image_path.len :0]; - - const kernelenv = w.kernel32.GetEnvironmentStringsW(); - defer { - if (kernelenv) |envptr| { - _ = w.kernel32.FreeEnvironmentStringsW(envptr); - } - } - - var size: usize = 0; - if (kernelenv) |pointer| { - // check that env is non-empty - if (pointer[0] != 0 or pointer[1] != 0) { - // array is terminated by two nulls - while (pointer[size] != 0 or pointer[size + 1] != 0) size += 1; - size += 1; - } - } - // now pointer[size] is the first null - - const envbuf = try allocator.alloc(u16, size + watcherChildEnv.len + 4); - defer allocator.free(envbuf); - if (kernelenv) |pointer| { - @memcpy(envbuf[0..size], pointer); - } - @memcpy(envbuf[size .. size + watcherChildEnv.len], watcherChildEnv); - envbuf[size + watcherChildEnv.len] = '='; - envbuf[size + watcherChildEnv.len + 1] = '1'; - envbuf[size + watcherChildEnv.len + 2] = 0; - envbuf[size + watcherChildEnv.len + 3] = 0; - - var startupinfo = windows.STARTUPINFOEXW{ - .StartupInfo = .{ - .cb = @sizeOf(windows.STARTUPINFOEXW), - .lpReserved = null, - .lpDesktop = null, - .lpTitle = null, - .dwX = 0, - .dwY = 0, - .dwXSize = 0, - .dwYSize = 0, - .dwXCountChars = 0, - .dwYCountChars = 0, - .dwFillAttribute = 0, - .dwFlags = w.STARTF_USESTDHANDLES, - .wShowWindow = 0, - .cbReserved2 = 0, - .lpReserved2 = null, - .hStdInput = std.io.getStdIn().handle, - .hStdOutput = std.io.getStdOut().handle, - .hStdError = std.io.getStdErr().handle, - }, - .lpAttributeList = p.ptr, - }; - @memset(std.mem.asBytes(procinfo), 0); - const rc = w.kernel32.CreateProcessW( - image_pathZ.ptr, - bun.windows.GetCommandLineW(), - null, - null, - 1, - flags, - envbuf.ptr, - null, - @ptrCast(&startupinfo), - procinfo, - ); - if (rc == 0) { - return error.Win32Error; - } - var is_in_job: w.BOOL = 0; - _ = windows.IsProcessInJob(procinfo.hProcess, job, &is_in_job); - assert(is_in_job != 0); - _ = std.os.windows.ntdll.NtClose(procinfo.hThread); - } -}; - -pub usingnamespace if (@import("builtin").target.os.tag != .windows) posix else win32; +pub const spawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn; pub fn isRegularFile(mode: anytype) bool { return S.ISREG(@intCast(mode)); diff --git a/src/c-headers-for-zig.h b/src/c-headers-for-zig.h index 512812aeff..6e791455d6 100644 --- a/src/c-headers-for-zig.h +++ b/src/c-headers-for-zig.h @@ -42,3 +42,8 @@ #include #include #endif + +#if WINDOWS +#include +#include +#endif diff --git a/src/c.zig b/src/c.zig index 96d5e0bdc5..57d1693adc 100644 --- a/src/c.zig +++ b/src/c.zig @@ -12,7 +12,7 @@ const std = @import("std"); const bun = @import("root").bun; const Environment = @import("./env.zig"); -pub const translated = @import("translated-c-headers"); +const translated = @import("translated-c-headers"); const PlatformSpecific = switch (Environment.os) { .mac => @import("./darwin_c.zig"), diff --git a/src/cli.zig b/src/cli.zig index 51a8162fb0..974d1b649d 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -1627,9 +1627,9 @@ pub const Command = struct { if (comptime Environment.isWindows) { if (global_cli_ctx.debug.hot_reload == .watch) { - if (!bun.isWatcherChild()) { + if (!bun.windows.isWatcherChild()) { // this is noreturn - bun.becomeWatcherManager(allocator); + bun.windows.becomeWatcherManager(allocator); } else { bun.auto_reload_on_crash = true; } diff --git a/src/cli/filter_run.zig b/src/cli/filter_run.zig index 4cf555481c..ab54fb10f7 100644 --- a/src/cli/filter_run.zig +++ b/src/cli/filter_run.zig @@ -411,7 +411,7 @@ const AbortHandler = struct { }; std.posix.sigaction(std.posix.SIG.INT, &action, null); } else { - const res = bun.windows.SetConsoleCtrlHandler(windowsCtrlHandler, std.os.windows.TRUE); + const res = bun.c.SetConsoleCtrlHandler(windowsCtrlHandler, std.os.windows.TRUE); if (res == 0) { if (Environment.isDebug) { Output.warn("Failed to set abort handler\n", .{}); @@ -424,7 +424,7 @@ const AbortHandler = struct { // only necessary on Windows, as on posix we pass the SA_RESETHAND flag if (Environment.isWindows) { // restores default Ctrl+C behavior - _ = bun.windows.SetConsoleCtrlHandler(null, std.os.windows.FALSE); + _ = bun.c.SetConsoleCtrlHandler(null, std.os.windows.FALSE); } } }; diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig index ca5c5923d1..13bb098475 100644 --- a/src/cli/init_command.zig +++ b/src/cli/init_command.zig @@ -41,11 +41,11 @@ pub const InitCommand = struct { // unset `ENABLE_VIRTUAL_TERMINAL_INPUT` on windows. This prevents backspace from // deleting the entire line const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; + bun.windows.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; defer if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode(bun.FD.stdin().native(), mode); + _ = bun.c.SetConsoleMode(bun.FD.stdin().native(), mode); } }; @@ -206,7 +206,7 @@ pub const InitCommand = struct { // Set raw mode to read single characters without echo const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(.std_in, .{ + bun.windows.updateStdioModeFlags(.std_in, .{ // virtual terminal input enables arrow keys, processed input lets ctrl+c kill the program .set = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT | bun.windows.ENABLE_PROCESSED_INPUT, // disabling line input sends keys immediately, disabling echo input makes sure it doesn't print to the terminal @@ -219,7 +219,7 @@ pub const InitCommand = struct { defer { if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode( + _ = bun.c.SetConsoleMode( bun.FD.stdin().native(), mode, ); diff --git a/src/cli/publish_command.zig b/src/cli/publish_command.zig index e453516414..42da6af852 100644 --- a/src/cli/publish_command.zig +++ b/src/cli/publish_command.zig @@ -680,11 +680,11 @@ pub const PublishCommand = struct { // unset `ENABLE_VIRTUAL_TERMINAL_INPUT` on windows. This prevents backspace from // deleting the entire line const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; + bun.windows.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; defer if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode(bun.FD.stdin().native(), mode); + _ = bun.c.SetConsoleMode(bun.FD.stdin().native(), mode); } }; diff --git a/src/codegen/process_windows_translate_c.zig b/src/codegen/process_windows_translate_c.zig new file mode 100644 index 0000000000..8bbfd5cf57 --- /dev/null +++ b/src/codegen/process_windows_translate_c.zig @@ -0,0 +1,72 @@ +// translate-c is unable to translate the unsuffixed windows functions +// like `SetCurrentDirectory` since they are defined with an odd macro +// that translate-c doesn't handle. +// +// #define SetCurrentDirectory __MINGW_NAME_AW(SetCurrentDirectory) +// +// In these cases, it's better to just reference the underlying function +// directly: SetCurrentDirectoryW. To make the error better, a post +// processing step is applied to the translate-c file. +const std = @import("std"); +const mem = std.mem; + +const symbol_replacements = std.StaticStringMap([]const u8).initComptime(&.{ + &.{ "NTSTATUS", "@import(\"std\").os.windows.NTSTATUS" }, + &.{ "HANDLE", "@import(\"std\").os.windows.HANDLE" }, + &.{ "PHANDLE", "*HANDLE" }, +}); + +pub fn main() !void { + const gpa = std.heap.smp_allocator; + var args = try std.process.argsWithAllocator(gpa); + errdefer args.deinit(); + assert(args.skip()); + + const in = brk: { + const in_path = args.next() orelse @panic("missing argument"); + const in = try std.fs.openFileAbsolute(in_path, .{}); + defer in.close(); + break :brk try in.readToEndAllocOptions(gpa, std.math.maxInt(u32), null, 1, 0); + }; + defer gpa.free(in); + + var out = try std.ArrayList(u8).initCapacity(gpa, in.len); + defer out.deinit(); + const w = out.writer(); + + var i: usize = 0; + while (mem.indexOfPos(u8, in, i, "pub const ")) |pub_i| { + var tokenizer = std.zig.Tokenizer.init(in); + tokenizer.index = pub_i + "pub const ".len; + const symbol_name_token = tokenizer.next(); + assert(symbol_name_token.tag == .identifier); + const symbol_name = in[symbol_name_token.loc.start..symbol_name_token.loc.end]; + try w.writeAll(in[i..symbol_name_token.loc.end]); + i = symbol_name_token.loc.end; + var end_of_line = mem.indexOfScalarPos(u8, in, symbol_name_token.loc.end, '\n') orelse in.len; + if (in[end_of_line - 1] != ';') { + // skip multiline decl + try w.writeAll(in[i..end_of_line]); + i = end_of_line; + continue; + } + end_of_line += 1; // include the \n + if (symbol_replacements.get(symbol_name)) |replace| { + try w.print(" = {s};\n", .{replace}); + } else if (mem.startsWith(u8, in[i..], " = __MINGW_NAME_AW(")) { + try w.print(" = @compileError(\"Use '{s}W' instead.\");\n", .{symbol_name}); + } else { + try w.writeAll(in[i..end_of_line]); + } + i = end_of_line; + } + try w.writeAll(in[i..]); + try std.fs.cwd().writeFile(.{ + .sub_path = args.next() orelse @panic("missing argument"), + .data = out.items, + }); +} + +fn assert(cond: bool) void { + if (!cond) @panic("unhandled"); +} diff --git a/src/crash_handler.zig b/src/crash_handler.zig index a2d32828ad..6911202725 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -294,7 +294,7 @@ pub fn crashHandler( if (std.os.windows.HRESULT_CODE(result) == .SUCCESS and name[0] != 0) { writer.print("({})", .{bun.fmt.utf16(bun.span(name))}) catch std.posix.abort(); } else { - writer.print("(thread {d})", .{bun.windows.GetCurrentThreadId()}) catch std.posix.abort(); + writer.print("(thread {d})", .{bun.c.GetCurrentThreadId()}) catch std.posix.abort(); } }, .mac, .linux => {}, diff --git a/src/deps/brotli_decoder.zig b/src/deps/brotli_c.zig similarity index 61% rename from src/deps/brotli_decoder.zig rename to src/deps/brotli_c.zig index 1634931b54..a490b8af87 100644 --- a/src/deps/brotli_decoder.zig +++ b/src/deps/brotli_c.zig @@ -1,3 +1,4 @@ +// TODO: prefer generating this file via translate_c const bun = @import("root").bun; const std = @import("std"); @@ -195,9 +196,138 @@ pub const BrotliDecoderParameter = enum(c_uint) { pub const BROTLI_UINT32_MAX = ~@import("std").zig.c_translation.cast(u32, @as(c_int, 0)); pub const BROTLI_SIZE_MAX = ~@import("std").zig.c_translation.cast(usize, @as(c_int, 0)); -pub const SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH = @as(c_int, 4); -pub const SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH = @as(c_int, 31); -pub const SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS = @as(c_int, 64); -pub const SHARED_BROTLI_MAX_COMPOUND_DICTS = @as(c_int, 15); pub const BROTLI_LAST_ERROR_CODE = BROTLI_DECODER_ERROR_UNREACHABLE; pub const BrotliSharedDictionaryStruct = struct_BrotliSharedDictionaryStruct; + +pub const struct_BrotliEncoderPreparedDictionaryStruct = opaque {}; +pub const BrotliEncoderPreparedDictionary = struct_BrotliEncoderPreparedDictionaryStruct; +extern fn BrotliSharedDictionaryCreateInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) ?*BrotliSharedDictionary; +extern fn BrotliSharedDictionaryDestroyInstance(dict: ?*BrotliSharedDictionary) void; +extern fn BrotliSharedDictionaryAttach(dict: ?*BrotliSharedDictionary, @"type": BrotliSharedDictionaryType, data_size: usize, data: [*c]const u8) c_int; +pub const BROTLI_MODE_GENERIC: c_int = 0; +pub const BROTLI_MODE_TEXT: c_int = 1; +pub const BROTLI_MODE_FONT: c_int = 2; +pub const BrotliEncoderMode = enum(c_uint) { + generic = 0, + text = 1, + font = 2, +}; +pub const BROTLI_OPERATION_PROCESS: c_int = 0; +pub const BROTLI_OPERATION_FLUSH: c_int = 1; +pub const BROTLI_OPERATION_FINISH: c_int = 2; +pub const BROTLI_OPERATION_EMIT_METADATA: c_int = 3; + +pub const BROTLI_PARAM_MODE: c_int = 0; +pub const BROTLI_PARAM_QUALITY: c_int = 1; +pub const BROTLI_PARAM_LGWIN: c_int = 2; +pub const BROTLI_PARAM_LGBLOCK: c_int = 3; +pub const BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: c_int = 4; +pub const BROTLI_PARAM_SIZE_HINT: c_int = 5; +pub const BROTLI_PARAM_LARGE_WINDOW: c_int = 6; +pub const BROTLI_PARAM_NPOSTFIX: c_int = 7; +pub const BROTLI_PARAM_NDIRECT: c_int = 8; +pub const BROTLI_PARAM_STREAM_OFFSET: c_int = 9; +pub const BrotliEncoderParameter = enum(c_uint) { + mode = 0, + quality = 1, + lgwin = 2, + lgblock = 3, + disable_literal_context_modeling = 4, + size_hint = 5, + large_window = 6, + npostfix = 7, + ndirect = 8, + stream_offset = 9, + // update kMaxBrotliParam in src/js/node/zlib.ts if this list changes +}; + +pub extern fn BrotliEncoderSetParameter(state: *BrotliEncoder, param: c_uint, value: u32) c_int; +pub extern fn BrotliEncoderCreateInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) ?*BrotliEncoder; +pub extern fn BrotliEncoderDestroyInstance(state: *BrotliEncoder) void; +pub extern fn BrotliEncoderPrepareDictionary(@"type": BrotliSharedDictionaryType, data_size: usize, data: [*c]const u8, quality: c_int, alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) *BrotliEncoderPreparedDictionary; +pub extern fn BrotliEncoderDestroyPreparedDictionary(dictionary: *BrotliEncoderPreparedDictionary) void; +pub extern fn BrotliEncoderAttachPreparedDictionary(state: *BrotliEncoder, dictionary: ?*const BrotliEncoderPreparedDictionary) c_int; +pub extern fn BrotliEncoderMaxCompressedSize(input_size: usize) usize; +pub extern fn BrotliEncoderCompress(quality: c_int, lgwin: c_int, mode: BrotliEncoderMode, input_size: usize, input_buffer: [*]const u8, encoded_size: *usize, encoded_buffer: [*]u8) c_int; +pub extern fn BrotliEncoderCompressStream(state: *BrotliEncoder, op: BrotliEncoder.Operation, available_in: *usize, next_in: *?[*]const u8, available_out: *usize, next_in: ?*?[*]u8, total_out: ?*usize) c_int; +pub extern fn BrotliEncoderIsFinished(state: *BrotliEncoder) c_int; +pub extern fn BrotliEncoderHasMoreOutput(state: *BrotliEncoder) c_int; +pub extern fn BrotliEncoderTakeOutput(state: *BrotliEncoder, size: *usize) ?[*]const u8; +pub extern fn BrotliEncoderEstimatePeakMemoryUsage(quality: c_int, lgwin: c_int, input_size: usize) usize; +pub extern fn BrotliEncoderGetPreparedDictionarySize(dictionary: ?*const BrotliEncoderPreparedDictionary) usize; +pub extern fn BrotliEncoderVersion() u32; + +pub const BrotliEncoder = opaque { + pub const Operation = enum(c_uint) { + process = 0, + flush = 1, + finish = 2, + emit_metadata = 3, + }; + + pub fn createInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) callconv(.C) ?*BrotliEncoder { + return BrotliEncoderCreateInstance(alloc_func, free_func, @"opaque"); + } + + pub fn destroyInstance(state: *BrotliEncoder) callconv(.C) void { + return BrotliEncoderDestroyInstance(state); + } + + pub fn hasMoreOutput(state: *BrotliEncoder) callconv(.C) bool { + return BrotliEncoderHasMoreOutput(state) > 0; + } + + pub fn takeOutput(state: *BrotliEncoder) []const u8 { + var size: usize = 0; + if (BrotliEncoderTakeOutput(state, &size)) |ptr| { + return ptr[0..size]; + } + + return ""; + } + + pub const CompressionResult = struct { + success: bool = false, + has_more: bool = false, + output: []const u8 = "", + }; + + // https://github.com/google/brotli/blob/2ad58d8603294f5ee33d23bb725e0e6a17c1de50/go/cbrotli/writer.go#L23-L40 + pub fn compressStream(state: *BrotliEncoder, op: Operation, data: []const u8) CompressionResult { + var available_in = data.len; + var next_in: ?[*]const u8 = data.ptr; + + var available_out: usize = 0; + + var result = CompressionResult{}; + + result.success = BrotliEncoderCompressStream(state, op, &available_in, &next_in, &available_out, null, null) > 0; + + if (result.success) { + result.output = takeOutput(state); + } + + result.has_more = BrotliEncoderHasMoreOutput(state) > 0; + + return result; + } + + pub fn setParameter(state: *BrotliEncoder, param: BrotliEncoderParameter, value: u32) bool { + return BrotliEncoderSetParameter(state, @intFromEnum(param), value) > 0; + } +}; + +pub const SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH = 4; +pub const SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH = 31; +pub const SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS = 64; +pub const SHARED_BROTLI_MAX_COMPOUND_DICTS = 15; +pub const BROTLI_MIN_WINDOW_BITS = 10; +pub const BROTLI_MAX_WINDOW_BITS = 24; +pub const BROTLI_LARGE_MAX_WINDOW_BITS = 30; +pub const BROTLI_MIN_INPUT_BLOCK_BITS = 16; +pub const BROTLI_MAX_INPUT_BLOCK_BITS = 24; +pub const BROTLI_MIN_QUALITY = 0; +pub const BROTLI_MAX_QUALITY = 11; +pub const BROTLI_DEFAULT_QUALITY = 11; +pub const BROTLI_DEFAULT_WINDOW = 22; +pub const BROTLI_DEFAULT_MODE = BROTLI_MODE_GENERIC; diff --git a/src/deps/brotli_encoder.zig b/src/deps/brotli_encoder.zig deleted file mode 100644 index 5143169724..0000000000 --- a/src/deps/brotli_encoder.zig +++ /dev/null @@ -1,143 +0,0 @@ -const bun = @import("root").bun; -const std = @import("std"); - -pub const brotli_alloc_func = ?*const fn (?*anyopaque, usize) callconv(.C) ?*anyopaque; -pub const brotli_free_func = ?*const fn (?*anyopaque, ?*anyopaque) callconv(.C) void; -pub const struct_BrotliSharedDictionaryStruct = opaque {}; -pub const BrotliSharedDictionary = struct_BrotliSharedDictionaryStruct; -pub const BROTLI_SHARED_DICTIONARY_RAW: c_int = 0; -pub const BROTLI_SHARED_DICTIONARY_SERIALIZED: c_int = 1; -pub const enum_BrotliSharedDictionaryType = c_uint; -pub const BrotliSharedDictionaryType = enum_BrotliSharedDictionaryType; -pub const struct_BrotliEncoderPreparedDictionaryStruct = opaque {}; -pub const BrotliEncoderPreparedDictionary = struct_BrotliEncoderPreparedDictionaryStruct; -extern fn BrotliSharedDictionaryCreateInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) ?*BrotliSharedDictionary; -extern fn BrotliSharedDictionaryDestroyInstance(dict: ?*BrotliSharedDictionary) void; -extern fn BrotliSharedDictionaryAttach(dict: ?*BrotliSharedDictionary, @"type": BrotliSharedDictionaryType, data_size: usize, data: [*c]const u8) c_int; -pub const BROTLI_MODE_GENERIC: c_int = 0; -pub const BROTLI_MODE_TEXT: c_int = 1; -pub const BROTLI_MODE_FONT: c_int = 2; -pub const BrotliEncoderMode = enum(c_uint) { - generic = 0, - text = 1, - font = 2, -}; -pub const BROTLI_OPERATION_PROCESS: c_int = 0; -pub const BROTLI_OPERATION_FLUSH: c_int = 1; -pub const BROTLI_OPERATION_FINISH: c_int = 2; -pub const BROTLI_OPERATION_EMIT_METADATA: c_int = 3; - -pub const BROTLI_PARAM_MODE: c_int = 0; -pub const BROTLI_PARAM_QUALITY: c_int = 1; -pub const BROTLI_PARAM_LGWIN: c_int = 2; -pub const BROTLI_PARAM_LGBLOCK: c_int = 3; -pub const BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: c_int = 4; -pub const BROTLI_PARAM_SIZE_HINT: c_int = 5; -pub const BROTLI_PARAM_LARGE_WINDOW: c_int = 6; -pub const BROTLI_PARAM_NPOSTFIX: c_int = 7; -pub const BROTLI_PARAM_NDIRECT: c_int = 8; -pub const BROTLI_PARAM_STREAM_OFFSET: c_int = 9; -pub const BrotliEncoderParameter = enum(c_uint) { - mode = 0, - quality = 1, - lgwin = 2, - lgblock = 3, - disable_literal_context_modeling = 4, - size_hint = 5, - large_window = 6, - npostfix = 7, - ndirect = 8, - stream_offset = 9, - // update kMaxBrotliParam in src/js/node/zlib.ts if this list changes -}; - -pub extern fn BrotliEncoderSetParameter(state: *BrotliEncoder, param: c_uint, value: u32) c_int; -pub extern fn BrotliEncoderCreateInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) ?*BrotliEncoder; -pub extern fn BrotliEncoderDestroyInstance(state: *BrotliEncoder) void; -pub extern fn BrotliEncoderPrepareDictionary(@"type": BrotliSharedDictionaryType, data_size: usize, data: [*c]const u8, quality: c_int, alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) *BrotliEncoderPreparedDictionary; -pub extern fn BrotliEncoderDestroyPreparedDictionary(dictionary: *BrotliEncoderPreparedDictionary) void; -pub extern fn BrotliEncoderAttachPreparedDictionary(state: *BrotliEncoder, dictionary: ?*const BrotliEncoderPreparedDictionary) c_int; -pub extern fn BrotliEncoderMaxCompressedSize(input_size: usize) usize; -pub extern fn BrotliEncoderCompress(quality: c_int, lgwin: c_int, mode: BrotliEncoderMode, input_size: usize, input_buffer: [*]const u8, encoded_size: *usize, encoded_buffer: [*]u8) c_int; -pub extern fn BrotliEncoderCompressStream(state: *BrotliEncoder, op: BrotliEncoder.Operation, available_in: *usize, next_in: *?[*]const u8, available_out: *usize, next_in: ?*?[*]u8, total_out: ?*usize) c_int; -pub extern fn BrotliEncoderIsFinished(state: *BrotliEncoder) c_int; -pub extern fn BrotliEncoderHasMoreOutput(state: *BrotliEncoder) c_int; -pub extern fn BrotliEncoderTakeOutput(state: *BrotliEncoder, size: *usize) ?[*]const u8; -pub extern fn BrotliEncoderEstimatePeakMemoryUsage(quality: c_int, lgwin: c_int, input_size: usize) usize; -pub extern fn BrotliEncoderGetPreparedDictionarySize(dictionary: ?*const BrotliEncoderPreparedDictionary) usize; -pub extern fn BrotliEncoderVersion() u32; - -pub const BrotliEncoder = opaque { - pub const Operation = enum(c_uint) { - process = 0, - flush = 1, - finish = 2, - emit_metadata = 3, - }; - - pub fn createInstance(alloc_func: brotli_alloc_func, free_func: brotli_free_func, @"opaque": ?*anyopaque) callconv(.C) ?*BrotliEncoder { - return BrotliEncoderCreateInstance(alloc_func, free_func, @"opaque"); - } - - pub fn destroyInstance(state: *BrotliEncoder) callconv(.C) void { - return BrotliEncoderDestroyInstance(state); - } - - pub fn hasMoreOutput(state: *BrotliEncoder) callconv(.C) bool { - return BrotliEncoderHasMoreOutput(state) > 0; - } - - pub fn takeOutput(state: *BrotliEncoder) []const u8 { - var size: usize = 0; - if (BrotliEncoderTakeOutput(state, &size)) |ptr| { - return ptr[0..size]; - } - - return ""; - } - - pub const CompressionResult = struct { - success: bool = false, - has_more: bool = false, - output: []const u8 = "", - }; - - // https://github.com/google/brotli/blob/2ad58d8603294f5ee33d23bb725e0e6a17c1de50/go/cbrotli/writer.go#L23-L40 - pub fn compressStream(state: *BrotliEncoder, op: Operation, data: []const u8) CompressionResult { - var available_in = data.len; - var next_in: ?[*]const u8 = data.ptr; - - var available_out: usize = 0; - - var result = CompressionResult{}; - - result.success = BrotliEncoderCompressStream(state, op, &available_in, &next_in, &available_out, null, null) > 0; - - if (result.success) { - result.output = takeOutput(state); - } - - result.has_more = BrotliEncoderHasMoreOutput(state) > 0; - - return result; - } - - pub fn setParameter(state: *BrotliEncoder, param: BrotliEncoderParameter, value: u32) bool { - return BrotliEncoderSetParameter(state, @intFromEnum(param), value) > 0; - } -}; - -pub const SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH = 4; -pub const SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH = 31; -pub const SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS = 64; -pub const SHARED_BROTLI_MAX_COMPOUND_DICTS = 15; -pub const BROTLI_MIN_WINDOW_BITS = 10; -pub const BROTLI_MAX_WINDOW_BITS = 24; -pub const BROTLI_LARGE_MAX_WINDOW_BITS = 30; -pub const BROTLI_MIN_INPUT_BLOCK_BITS = 16; -pub const BROTLI_MAX_INPUT_BLOCK_BITS = 24; -pub const BROTLI_MIN_QUALITY = 0; -pub const BROTLI_MAX_QUALITY = 11; -pub const BROTLI_DEFAULT_QUALITY = 11; -pub const BROTLI_DEFAULT_WINDOW = 22; -pub const BROTLI_DEFAULT_MODE = BROTLI_MODE_GENERIC; diff --git a/src/fd.zig b/src/fd.zig index 98f6f1b0c3..3719afabc3 100644 --- a/src/fd.zig +++ b/src/fd.zig @@ -266,7 +266,7 @@ pub const FD = packed struct(backing_int) { null; }, .windows => |handle| result: { - break :result switch (bun.windows.NtClose(handle)) { + break :result switch (bun.c.NtClose(handle)) { .SUCCESS => null, else => |rc| bun.sys.Error{ .errno = if (bun.windows.Win32Error.fromNTStatus(rc).toSystemErrno()) |errno| @intFromEnum(errno) else 1, diff --git a/src/output.zig b/src/output.zig index cff81f62e6..d2b5ae9821 100644 --- a/src/output.zig +++ b/src/output.zig @@ -10,6 +10,7 @@ const ComptimeStringMap = bun.ComptimeStringMap; const use_mimalloc = bun.use_mimalloc; const writeStream = std.json.writeStream; const WriteStream = std.json.WriteStream; +const c = bun.c; const SystemTimer = @import("./system_timer.zig").Timer; @@ -170,15 +171,15 @@ pub const Source = struct { const handles = &.{ &stdin, &stdout, &stderr }; inline for (console_mode, handles) |mode, handle| { if (mode) |m| { - _ = w.SetConsoleMode(handle.*, m); + _ = c.SetConsoleMode(handle.*, m); } } if (console_output_codepage != 0) - _ = w.kernel32.SetConsoleOutputCP(console_output_codepage); + _ = c.SetConsoleOutputCP(console_output_codepage); if (console_codepage != 0) - _ = w.SetConsoleCP(console_codepage); + _ = c.SetConsoleCP(console_codepage); } pub fn init() void { @@ -199,14 +200,14 @@ pub const Source = struct { // https://learn.microsoft.com/en-us/windows/console/setconsoleoutputcp const CP_UTF8 = 65001; - console_output_codepage = w.kernel32.GetConsoleOutputCP(); - _ = w.kernel32.SetConsoleOutputCP(CP_UTF8); + console_output_codepage = c.GetConsoleOutputCP(); + _ = c.SetConsoleOutputCP(CP_UTF8); - console_codepage = w.kernel32.GetConsoleOutputCP(); - _ = w.SetConsoleCP(CP_UTF8); + console_codepage = c.GetConsoleOutputCP(); + _ = c.SetConsoleCP(CP_UTF8); var mode: w.DWORD = undefined; - if (w.kernel32.GetConsoleMode(stdin, &mode) != 0) { + if (c.GetConsoleMode(stdin, &mode) != 0) { console_mode[0] = mode; bun_stdio_tty[0] = 1; // There are no flags to set on standard in, but just in case something @@ -216,16 +217,16 @@ pub const Source = struct { // intentionally set for any purpose, and instead only caused problems. } - if (w.kernel32.GetConsoleMode(stdout, &mode) != 0) { + if (c.GetConsoleMode(stdout, &mode) != 0) { console_mode[1] = mode; bun_stdio_tty[1] = 1; - _ = w.SetConsoleMode(stdout, w.ENABLE_PROCESSED_OUTPUT | std.os.windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | w.ENABLE_WRAP_AT_EOL_OUTPUT | mode); + _ = c.SetConsoleMode(stdout, w.ENABLE_PROCESSED_OUTPUT | std.os.windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | w.ENABLE_WRAP_AT_EOL_OUTPUT | mode); } - if (w.kernel32.GetConsoleMode(stderr, &mode) != 0) { + if (c.GetConsoleMode(stderr, &mode) != 0) { console_mode[2] = mode; bun_stdio_tty[2] = 1; - _ = w.SetConsoleMode(stderr, w.ENABLE_PROCESSED_OUTPUT | std.os.windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | w.ENABLE_WRAP_AT_EOL_OUTPUT | mode); + _ = c.SetConsoleMode(stderr, w.ENABLE_PROCESSED_OUTPUT | std.os.windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING | w.ENABLE_WRAP_AT_EOL_OUTPUT | mode); } } }; @@ -881,8 +882,7 @@ pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) [:0]const u8 { @setEvalBranchQuota(9999); comptime var i: usize = 0; comptime while (i < fmt.len) { - const c = fmt[i]; - switch (c) { + switch (fmt[i]) { '\\' => { i += 1; if (i < fmt.len) { diff --git a/src/sys.zig b/src/sys.zig index 57ab45c6a5..79ad73362c 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -8,7 +8,8 @@ const posix = std.posix; const assertIsValidWindowsPath = bun.strings.assertIsValidWindowsPath; const default_allocator = bun.default_allocator; -const kernel32 = bun.windows; +const kernel32 = bun.windows.kernel32; +const ntdll = bun.windows.ntdll; const mem = std.mem; const page_size_min = std.heap.page_size_min; const mode_t = posix.mode_t; @@ -43,7 +44,7 @@ pub const syscall = switch (Environment.os) { else => @compileError("not implemented"), }; -/// Non-cancellable verisons of various libc functions are undocumented +/// Non-cancellable versions of various libc functions are undocumented const darwin_nocancel = struct { const c = std.c; pub extern "c" fn @"recvfrom$NOCANCEL"(sockfd: c.fd_t, noalias buf: *anyopaque, len: usize, flags: u32, noalias src_addr: ?*c.sockaddr, noalias addrlen: ?*c.socklen_t) isize; @@ -723,7 +724,7 @@ pub fn chdirOSPath(path: bun.stringZ, destination: if (Environment.isPosix) bun. if (comptime Environment.isWindows) { const wbuf = bun.WPathBufferPool.get(); defer bun.WPathBufferPool.put(wbuf); - if (kernel32.SetCurrentDirectory(bun.strings.toWDirPath(wbuf, destination)) == windows.FALSE) { + if (bun.c.SetCurrentDirectoryW(bun.strings.toWDirPath(wbuf, destination)) == windows.FALSE) { log("SetCurrentDirectory({s}) = {d}", .{ destination, kernel32.GetLastError() }); return Maybe(void).errnoSysPD(0, .chdir, path, destination) orelse Maybe(void).success; } @@ -818,9 +819,9 @@ pub fn statfs(path: [:0]const u8) Maybe(bun.StatFS) { } else { var statfs_ = mem.zeroes(bun.StatFS); const rc = if (Environment.isLinux) - C.translated.statfs(path, &statfs_) + bun.c.statfs(path, &statfs_) else if (Environment.isMac) - C.translated.statfs(path, &statfs_) + bun.c.statfs(path, &statfs_) else @compileError("Unsupported platform"); @@ -980,8 +981,7 @@ pub fn mkdirOSPath(file_path: bun.OSPathSliceZ, flags: mode_t) Maybe(void) { return switch (Environment.os) { else => mkdir(file_path, flags), .windows => { - const rc = kernel32.CreateDirectoryW(file_path, null); - + const rc = bun.c.CreateDirectoryW(file_path, null); if (Maybe(void).errnoSys( rc, .mkdir, @@ -3341,21 +3341,21 @@ pub fn existsAtType(fd: bun.FileDescriptor, subpath: anytype) Maybe(ExistsAtType .SecurityQualityOfService = null, }; var basic_info: w.FILE_BASIC_INFORMATION = undefined; - const rc = kernel32.NtQueryAttributesFile(&attr, &basic_info); + const rc = ntdll.NtQueryAttributesFile(&attr, &basic_info); if (JSC.Maybe(bool).errnoSys(rc, .access)) |err| { syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = {}", .{ bun.fmt.fmtOSPath(path, .{}), err }); return .{ .err = err.err }; } - const is_regular_file = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and + const is_regular_file = basic_info.FileAttributes != bun.c.INVALID_FILE_ATTRIBUTES and // from libuv: directories cannot be read-only // https://github.com/libuv/libuv/blob/eb5af8e3c0ea19a6b0196d5db3212dae1785739b/src/win/fs.c#L2144-L2146 - (basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY == 0 or - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0); + (basic_info.FileAttributes & bun.c.FILE_ATTRIBUTE_DIRECTORY == 0 or + basic_info.FileAttributes & bun.c.FILE_ATTRIBUTE_READONLY == 0); - const is_dir = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY != 0 and - basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_READONLY == 0; + const is_dir = basic_info.FileAttributes != bun.c.INVALID_FILE_ATTRIBUTES and + basic_info.FileAttributes & bun.c.FILE_ATTRIBUTE_DIRECTORY != 0 and + basic_info.FileAttributes & bun.c.FILE_ATTRIBUTE_READONLY == 0; return if (is_dir) { syslog("NtQueryAttributesFile({}, O_RDONLY, 0) = directory", .{bun.fmt.fmtOSPath(path, .{})}); @@ -3746,7 +3746,7 @@ pub fn writeNonblocking(fd: bun.FileDescriptor, buf: []const u8) Maybe(usize) { pub fn getFileSize(fd: bun.FileDescriptor) Maybe(usize) { if (Environment.isWindows) { var size: windows.LARGE_INTEGER = undefined; - if (windows.GetFileSizeEx(fd.cast(), &size) == windows.FALSE) { + if (windows.kernel32.GetFileSizeEx(fd.cast(), &size) == windows.FALSE) { const err = Error.fromCode(windows.getLastErrno(), .fstat); log("GetFileSizeEx({}) = {s}", .{ fd, err.name() }); return .{ .err = err }; @@ -3967,7 +3967,7 @@ pub const File = struct { if (Environment.isWindows) { const rt = windows.GetFileType(self.handle.cast()); if (rt == windows.FILE_TYPE_UNKNOWN) { - switch (bun.windows.GetLastError()) { + switch (windows.GetLastError()) { .SUCCESS => {}, else => |err| { return .{ .err = Error.fromCode((bun.C.SystemErrno.init(err) orelse bun.C.SystemErrno.EUNKNOWN).toE(), .fstat) }; diff --git a/src/windows.zig b/src/windows.zig index d83fa39597..cfb4934bae 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -1,6 +1,13 @@ const bun = @import("root").bun; +const Output = bun.Output; const windows = std.os.windows; const win32 = windows; + +const c = bun.c; +pub const ntdll = windows.ntdll; +pub const kernel32 = windows.kernel32; +pub const GetLastError = kernel32.GetLastError; + pub const PATH_MAX_WIDE = windows.PATH_MAX_WIDE; pub const MAX_PATH = windows.MAX_PATH; pub const WORD = windows.WORD; @@ -38,7 +45,6 @@ pub const FILETIME = windows.FILETIME; pub const DUPLICATE_SAME_ACCESS = windows.DUPLICATE_SAME_ACCESS; pub const OBJECT_ATTRIBUTES = windows.OBJECT_ATTRIBUTES; -pub const kernel32 = windows.kernel32; pub const IO_STATUS_BLOCK = windows.IO_STATUS_BLOCK; pub const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS; pub const FILE_SHARE_READ = windows.FILE_SHARE_READ; @@ -62,9 +68,6 @@ pub const FILE_WRITE_THROUGH = windows.FILE_WRITE_THROUGH; pub const FILE_SEQUENTIAL_ONLY = windows.FILE_SEQUENTIAL_ONLY; pub const FILE_SYNCHRONOUS_IO_NONALERT = windows.FILE_SYNCHRONOUS_IO_NONALERT; pub const FILE_OPEN_REPARSE_POINT = windows.FILE_OPEN_REPARSE_POINT; -pub usingnamespace kernel32; -pub const ntdll = windows.ntdll; -pub usingnamespace ntdll; pub const user32 = windows.user32; pub const advapi32 = windows.advapi32; @@ -3414,7 +3417,7 @@ pub fn GetFinalPathNameByHandle( }); if (return_length == 0) { - bun.sys.syslog("GetFinalPathNameByHandleW({*p}) = {}", .{ hFile, bun.windows.GetLastError() }); + bun.sys.syslog("GetFinalPathNameByHandleW({*p}) = {}", .{ hFile, GetLastError() }); return error.FileNotFound; } @@ -3478,7 +3481,6 @@ pub const ENABLE_VIRTUAL_TERMINAL_INPUT = 0x200; pub const ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002; pub const ENABLE_PROCESSED_OUTPUT = 0x0001; -const SetConsoleMode = kernel32.SetConsoleMode; pub extern fn SetStdHandle(nStdHandle: u32, hHandle: *anyopaque) u32; pub extern fn GetConsoleOutputCP() u32; pub extern "kernel32" fn SetConsoleCP(wCodePageID: std.os.windows.UINT) callconv(std.os.windows.WINAPI) std.os.windows.BOOL; @@ -3667,3 +3669,188 @@ pub extern "kernel32" fn GetCommandLineW() callconv(.winapi) LPWSTR; pub extern "kernel32" fn CreateDirectoryW(lpPathName: [*:0]const u16, lpSecurityAttributes: ?*windows.SECURITY_ATTRIBUTES) callconv(.winapi) BOOL; pub extern "kernel32" fn SetEndOfFile(hFile: HANDLE) callconv(.winapi) BOOL; pub extern "kernel32" fn GetProcessTimes(in_hProcess: HANDLE, out_lpCreationTime: *FILETIME, out_lpExitTime: *FILETIME, out_lpKernelTime: *FILETIME, out_lpUserTime: *FILETIME) callconv(.winapi) BOOL; + +/// Returns the original mode, or null on failure +pub fn updateStdioModeFlags(i: bun.FD.Stdio, opts: struct { set: DWORD = 0, unset: DWORD = 0 }) !DWORD { + const fd = i.fd(); + var original_mode: DWORD = 0; + if (c.GetConsoleMode(fd.cast(), &original_mode) != 0) { + if (c.SetConsoleMode(fd.cast(), (original_mode | opts.set) & ~opts.unset) == 0) { + return getLastError(); + } + } else return getLastError(); + return original_mode; +} + +const watcherChildEnv: [:0]const u16 = bun.strings.toUTF16Literal("_BUN_WATCHER_CHILD"); + +// magic exit code to indicate to the watcher manager that the child process should be re-spawned +// this was randomly generated - we need to avoid using a common exit code that might be used by the script itself +pub const watcher_reload_exit: DWORD = 3224497970; + +pub const spawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn; + +pub fn isWatcherChild() bool { + var buf: [1]u16 = undefined; + return c.GetEnvironmentVariableW(@constCast(watcherChildEnv.ptr), &buf, 1) > 0; +} + +pub fn becomeWatcherManager(allocator: std.mem.Allocator) noreturn { + // this process will be the parent of the child process that actually runs the script + var procinfo: std.os.windows.PROCESS_INFORMATION = undefined; + bun.C.windows_enable_stdio_inheritance(); + const job = CreateJobObjectA(null, null) orelse Output.panic( + "Could not create watcher Job Object: {s}", + .{@tagName(std.os.windows.kernel32.GetLastError())}, + ); + var jeli = std.mem.zeroes(c.JOBOBJECT_EXTENDED_LIMIT_INFORMATION); + jeli.BasicLimitInformation.LimitFlags = + c.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | + c.JOB_OBJECT_LIMIT_BREAKAWAY_OK | + c.JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK | + c.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; + if (c.SetInformationJobObject( + job, + c.JobObjectExtendedLimitInformation, + &jeli, + @sizeOf(c.JOBOBJECT_EXTENDED_LIMIT_INFORMATION), + ) == 0) { + Output.panic( + "Could not configure watcher Job Object: {s}", + .{@tagName(std.os.windows.kernel32.GetLastError())}, + ); + } + + while (true) { + spawnWatcherChild(allocator, &procinfo, job) catch |err| { + bun.handleErrorReturnTrace(err, @errorReturnTrace()); + if (err == error.Win32Error) { + Output.panic("Failed to spawn process: {s}\n", .{@tagName(GetLastError())}); + } + Output.panic("Failed to spawn process: {s}\n", .{@errorName(err)}); + }; + windows.WaitForSingleObject(procinfo.hProcess, c.INFINITE) catch |err| { + Output.panic("Failed to wait for child process: {s}\n", .{@errorName(err)}); + }; + var exit_code: DWORD = 0; + if (c.GetExitCodeProcess(procinfo.hProcess, &exit_code) == 0) { + const err = windows.GetLastError(); + _ = c.NtClose(procinfo.hProcess); + Output.panic("Failed to get exit code of child process: {s}\n", .{@tagName(err)}); + } + _ = c.NtClose(procinfo.hProcess); + + // magic exit code to indicate that the child process should be re-spawned + if (exit_code == watcher_reload_exit) { + continue; + } else { + bun.Global.exit(exit_code); + } + } +} + +pub fn spawnWatcherChild( + allocator: std.mem.Allocator, + procinfo: *std.os.windows.PROCESS_INFORMATION, + job: HANDLE, +) !void { + // https://devblogs.microsoft.com/oldnewthing/20230209-00/?p=107812 + var attr_size: usize = undefined; + _ = InitializeProcThreadAttributeList(null, 1, 0, &attr_size); + const p = try allocator.alloc(u8, attr_size); + defer allocator.free(p); + if (InitializeProcThreadAttributeList(p.ptr, 1, 0, &attr_size) == 0) { + return error.Win32Error; + } + if (UpdateProcThreadAttribute( + p.ptr, + 0, + c.PROC_THREAD_ATTRIBUTE_JOB_LIST, + @ptrCast(&job), + @sizeOf(HANDLE), + null, + null, + ) == 0) { + return error.Win32Error; + } + + const flags: DWORD = c.CREATE_UNICODE_ENVIRONMENT | c.EXTENDED_STARTUPINFO_PRESENT; + + const image_path = exePathW(); + var wbuf: WPathBuffer = undefined; + @memcpy(wbuf[0..image_path.len], image_path); + wbuf[image_path.len] = 0; + + const image_pathZ = wbuf[0..image_path.len :0]; + + const kernelenv = kernel32.GetEnvironmentStringsW(); + defer if (kernelenv) |envptr| { + _ = kernel32.FreeEnvironmentStringsW(envptr); + }; + + var size: usize = 0; + if (kernelenv) |pointer| { + // check that env is non-empty + if (pointer[0] != 0 or pointer[1] != 0) { + // array is terminated by two nulls + while (pointer[size] != 0 or pointer[size + 1] != 0) size += 1; + size += 1; + } + } + // now pointer[size] is the first null + + const envbuf = try allocator.alloc(u16, size + watcherChildEnv.len + 4); + defer allocator.free(envbuf); + if (kernelenv) |pointer| { + @memcpy(envbuf[0..size], pointer); + } + @memcpy(envbuf[size .. size + watcherChildEnv.len], watcherChildEnv); + envbuf[size + watcherChildEnv.len] = '='; + envbuf[size + watcherChildEnv.len + 1] = '1'; + envbuf[size + watcherChildEnv.len + 2] = 0; + envbuf[size + watcherChildEnv.len + 3] = 0; + + var startupinfo = STARTUPINFOEXW{ + .StartupInfo = .{ + .cb = @sizeOf(STARTUPINFOEXW), + .lpReserved = null, + .lpDesktop = null, + .lpTitle = null, + .dwX = 0, + .dwY = 0, + .dwXSize = 0, + .dwYSize = 0, + .dwXCountChars = 0, + .dwYCountChars = 0, + .dwFillAttribute = 0, + .dwFlags = c.STARTF_USESTDHANDLES, + .wShowWindow = 0, + .cbReserved2 = 0, + .lpReserved2 = null, + .hStdInput = std.io.getStdIn().handle, + .hStdOutput = std.io.getStdOut().handle, + .hStdError = std.io.getStdErr().handle, + }, + .lpAttributeList = p.ptr, + }; + @memset(std.mem.asBytes(procinfo), 0); + const rc = kernel32.CreateProcessW( + image_pathZ.ptr, + c.GetCommandLineW(), + null, + null, + 1, + flags, + envbuf.ptr, + null, + @ptrCast(&startupinfo), + procinfo, + ); + if (rc == 0) { + return error.Win32Error; + } + var is_in_job: c.BOOL = 0; + _ = c.IsProcessInJob(procinfo.hProcess, job, &is_in_job); + bun.debugAssert(is_in_job != 0); + _ = c.NtClose(procinfo.hThread); +} diff --git a/test/internal/ban-words.test.ts b/test/internal/ban-words.test.ts index 22760d897e..3e49b39870 100644 --- a/test/internal/ban-words.test.ts +++ b/test/internal/ban-words.test.ts @@ -46,6 +46,7 @@ for (const file of files) { if (file.isDirectory()) continue; if (!file.name.endsWith(".zig")) continue; if (file.parentPath.startsWith("src" + path.sep + "deps")) continue; + if (file.parentPath.startsWith("src" + path.sep + "codegen")) continue; const content = await Bun.file(file.parentPath + path.sep + file.name).text(); for (const word of words_keys) { let regex = words[word].regex ? new RegExp(word, "g") : undefined;