From 1fe1b2d23c93a252babf7606e74215acdabf5013 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Sat, 25 Jul 2026 11:48:26 +0200 Subject: [PATCH 1/3] build: fix static libghostty-vt linking on Windows This PR fixes static linking for libghostty-vt on Windows by propagating a couple of missing dependencies (discovered while running Neovim's Zig build, see https://github.com/neovim/neovim/actions/runs/30130848061/job/89604799965?pr=39773). --- .github/workflows/test.yml | 4 ++++ example/c-vt-static/build.zig | 1 + src/build/GhosttyLibVt.zig | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8e82a2853..bc0de2691 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -742,6 +742,10 @@ jobs: - name: Build libghostty-vt run: zig build -Demit-lib-vt + - name: Test static linking + working-directory: example/c-vt-static + run: zig build + build-libghostty-windows-gnu: runs-on: namespace-profile-ghostty-windows timeout-minutes: 45 diff --git a/example/c-vt-static/build.zig b/example/c-vt-static/build.zig index 0e53d69c5..23f6f9d0f 100644 --- a/example/c-vt-static/build.zig +++ b/example/c-vt-static/build.zig @@ -14,6 +14,7 @@ pub fn build(b: *std.Build) void { .root = b.path("src"), .files = &.{"main.c"}, }); + exe_mod.addCMacro("GHOSTTY_STATIC", ""); // You'll want to use a lazy dependency here so that ghostty is only // downloaded if you actually need it. diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig index b3b89a88e..389be4ef5 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -247,6 +247,14 @@ fn initLib( // Zig's ubsan emits /exclude-symbols linker directives that // are incompatible with the MSVC linker (LNK4229). lib.bundle_ubsan_rt = false; + + if (kind == .static) { + // compiler_rt expects ntdll to provide _fltused when libc is + // linked, and the Zig standard library uses other NT and kernel32 + // symbols. + lib.root_module.linkSystemLibrary("ntdll", .{}); + lib.root_module.linkSystemLibrary("kernel32", .{}); + } } if (lib.rootModuleTarget().abi.isAndroid()) { From 84254a9d8cb7d8bd28852933484be3f499cfbeee Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Sat, 25 Jul 2026 16:35:50 +0200 Subject: [PATCH 2/3] build: avoid MSVC C++ runtime in no-libcxx builds AI-assisted: Codex --- pkg/highway/build.zig | 7 +++++++ pkg/simdutf/build.zig | 14 ++++++++++---- src/build/GhosttyLibVt.zig | 11 ++++++++--- src/build/SharedDeps.zig | 5 +++++ src/lib_vt.zig | 11 +++++++++++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index fce823bf3..af3300eb3 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -99,6 +99,13 @@ pub fn build(b: *std.Build) !void { "-fmath-errno", "-fno-exceptions", }); + } else if (target.result.abi == .msvc) { + try flags.appendSlice(b.allocator, &.{ + // -fno-autolink also drops UCRT's /alternatename fallback. + "-D_Avx2WmemEnabledWeakValue=_Avx2WmemEnabled", + "-fno-autolink", + "-fno-stack-protector", + }); } lib.root_module.addCSourceFiles(.{ .flags = flags.items, .files = &.{ diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index a92fa3a07..3efc08263 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -50,10 +50,16 @@ pub fn build(b: *std.Build) !void { if (no_libcxx) { try flags.append(b.allocator, "-DSIMDUTF_NO_LIBCXX"); - if (target.result.abi != .msvc) { - // Clang/GCC-only flags; MSVC doesn't accept these. - try flags.append(b.allocator, "-fno-exceptions"); - try flags.append(b.allocator, "-fno-rtti"); + try flags.append(b.allocator, "-fno-exceptions"); + try flags.append(b.allocator, "-fno-rtti"); + if (target.result.abi == .msvc) { + try flags.appendSlice(b.allocator, &.{ + "-D_USE_STD_VECTOR_ALGORITHMS=0", + // -fno-autolink also drops UCRT's /alternatename fallback. + "-D_Avx2WmemEnabledWeakValue=_Avx2WmemEnabled", + "-fno-autolink", + "-fno-stack-protector", + }); } lib.root_module.addCMacro("SIMDUTF_NO_LIBCXX", "1"); diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig index 389be4ef5..0ef7d6937 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -249,9 +249,14 @@ fn initLib( lib.bundle_ubsan_rt = false; if (kind == .static) { - // compiler_rt expects ntdll to provide _fltused when libc is - // linked, and the Zig standard library uses other NT and kernel32 - // symbols. + if (target.result.abi == .msvc) { + // Zig's compiler runtime doesn't provide MSVC's security + // cookie symbols when libc is linked. Disable stack-protector + // generation so static consumers don't need BufferOverflowU. + lib.root_module.stack_protector = false; + } + + // The Zig standard library uses NT and kernel32 symbols. lib.root_module.linkSystemLibrary("ntdll", .{}); lib.root_module.linkSystemLibrary("kernel32", .{}); } diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index f1ec7c9d5..0c1589842 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -991,6 +991,11 @@ pub fn addSimd( "-fno-sanitize=undefined", "-fno-sanitize-trap=undefined", }); + if (target.result.abi == .msvc) try flags.appendSlice(b.allocator, &.{ + // -fno-autolink also drops UCRT's /alternatename fallback. + "-D_Avx2WmemEnabledWeakValue=_Avx2WmemEnabled", + "-fno-autolink", + }); m.addCSourceFiles(.{ .files = &.{ diff --git a/src/lib_vt.zig b/src/lib_vt.zig index b4a2722bf..e49427409 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -12,6 +12,8 @@ const lib = @This(); const std = @import("std"); const builtin = @import("builtin"); +var msvc_fltused: c_int = 1; + // The public API below reproduces a lot of terminal/main.zig but // is separate because (1) we need our root file to be in `src/` // so we can access other directories and (2) we may want to withhold @@ -145,6 +147,15 @@ comptime { // If we're building the C library (vs. the Zig module) then // we want to reference the C API so that it gets exported. if (@import("root") == lib) { + // MSVC requires this marker whenever floating-point code is present. + // Zig's compiler_rt only provides it when libc is not linked. + if (builtin.os.tag == .windows and + builtin.abi == .msvc and + builtin.link_mode == .static) + { + @export(&msvc_fltused, .{ .name = "_fltused" }); + } + // Force-reference our memset override so its export is // emitted. This must stay inside the root guard so that // downstream Zig module consumers don't get the override From 3b4600014c0e897acd2db469af6e101f1f8645eb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 26 Jul 2026 15:38:16 -0700 Subject: [PATCH 3/3] clarify comments --- src/lib_vt.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib_vt.zig b/src/lib_vt.zig index e49427409..0d9fda7c6 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -12,8 +12,6 @@ const lib = @This(); const std = @import("std"); const builtin = @import("builtin"); -var msvc_fltused: c_int = 1; - // The public API below reproduces a lot of terminal/main.zig but // is separate because (1) we need our root file to be in `src/` // so we can access other directories and (2) we may want to withhold @@ -143,6 +141,9 @@ pub const unicode = struct { pub const graphemeWidth = unicode_pkg.graphemeWidth; }; +/// Used for MSVC builds (see below) +var msvc_fltused: c_int = 1; + comptime { // If we're building the C library (vs. the Zig module) then // we want to reference the C API so that it gets exported. @@ -153,7 +154,10 @@ comptime { builtin.abi == .msvc and builtin.link_mode == .static) { - @export(&msvc_fltused, .{ .name = "_fltused" }); + @export( + &msvc_fltused, + .{ .name = "_fltused" }, + ); } // Force-reference our memset override so its export is