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/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 b3b89a88e..0ef7d6937 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -247,6 +247,19 @@ 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) { + 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", .{}); + } } if (lib.rootModuleTarget().abi.isAndroid()) { 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..0d9fda7c6 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -141,10 +141,25 @@ 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. 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