From 3d581eb92eade74bce9becdda50c0bf2877df366 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 23 Mar 2026 12:05:57 -0700 Subject: [PATCH] build: use linkLibC instead of linkLibCpp on MSVC targets When compiling C++ files, Zig unconditionally passes -nostdinc++ and, if link_libcpp is set, adds its bundled libc++/libc++abi include paths as replacements (see Compilation.zig). On MSVC targets this conflicts with the MSVC C++ runtime headers (vcruntime_typeinfo.h, vcruntime_exception.h, etc.), causing compilation failures in SIMD C++ code. The fix is to use linkLibC instead of linkLibCpp on MSVC. Zig always passes -nostdinc to strip default search paths, but LibCDirs.detect re-adds the MSVC SDK include directories, which contain both C and C++ standard library headers. This gives us proper access to MSVC's own , , , etc. without the libc++ conflicts. For the package builds (highway, simdutf, utfcpp) this means switching from linkLibCpp to linkLibC on MSVC. For SharedDeps and GhosttyZig, linkLibC is already called separately, so we just skip linkLibCpp. --- pkg/highway/build.zig | 8 ++++++-- pkg/simdutf/build.zig | 8 ++++++-- pkg/utfcpp/build.zig | 8 ++++++-- src/build/GhosttyZig.zig | 6 ++++-- src/build/SharedDeps.zig | 9 ++++++--- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index 0cc816992..49656b93e 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -20,8 +20,12 @@ pub fn build(b: *std.Build) !void { }), .linkage = .static, }); - // On MSVC, the C++ standard library is provided by the MSVC runtime - // and linking Zig's bundled libc++ would conflict with it. + lib.linkLibC(); + // On MSVC, we must not use linkLibCpp because Zig unconditionally + // passes -nostdinc++ and then adds its bundled libc++/libc++abi + // include paths, which conflict with MSVC's own C++ runtime headers. + // The MSVC SDK include directories (added via linkLibC) contain + // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { lib.linkLibCpp(); } diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index 6a9445bfe..e132507a1 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -12,8 +12,12 @@ pub fn build(b: *std.Build) !void { }), .linkage = .static, }); - // On MSVC, the C++ standard library is provided by the MSVC runtime - // and linking Zig's bundled libc++ would conflict with it. + lib.linkLibC(); + // On MSVC, we must not use linkLibCpp because Zig unconditionally + // passes -nostdinc++ and then adds its bundled libc++/libc++abi + // include paths, which conflict with MSVC's own C++ runtime headers. + // The MSVC SDK include directories (added via linkLibC) contain + // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { lib.linkLibCpp(); } diff --git a/pkg/utfcpp/build.zig b/pkg/utfcpp/build.zig index 27b0ebaa2..15c652c14 100644 --- a/pkg/utfcpp/build.zig +++ b/pkg/utfcpp/build.zig @@ -12,8 +12,12 @@ pub fn build(b: *std.Build) !void { }), .linkage = .static, }); - // On MSVC, the C++ standard library is provided by the MSVC runtime - // and linking Zig's bundled libc++ would conflict with it. + lib.linkLibC(); + // On MSVC, we must not use linkLibCpp because Zig unconditionally + // passes -nostdinc++ and then adds its bundled libc++/libc++abi + // include paths, which conflict with MSVC's own C++ runtime headers. + // The MSVC SDK include directories (added via linkLibC) contain + // both C and C++ headers, so linkLibCpp is not needed. if (target.result.abi != .msvc) { lib.linkLibCpp(); } diff --git a/src/build/GhosttyZig.zig b/src/build/GhosttyZig.zig index 3a40e521e..aabc00d46 100644 --- a/src/build/GhosttyZig.zig +++ b/src/build/GhosttyZig.zig @@ -64,8 +64,10 @@ fn initVt( .optimize = cfg.optimize, // SIMD require libc/libcpp (both) but otherwise we don't care. - // On MSVC, the C++ standard library is provided by the MSVC runtime - // and linking libc++ would conflict with it. + // On MSVC, we must not use linkLibCpp because Zig passes + // -nostdinc++ and adds its bundled libc++/libc++abi headers + // which conflict with MSVC's C++ runtime. The MSVC SDK dirs + // added via link_libc contain both C and C++ headers. .link_libc = if (cfg.simd) true else null, .link_libcpp = if (cfg.simd and cfg.target.result.abi != .msvc) true else null, }); diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index 475bf626f..72e88f757 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -399,9 +399,12 @@ pub fn add( step.addIncludePath(b.path("src/apprt/gtk")); } - // libcpp is required for various dependencies. On MSVC, the C++ - // standard library is provided by the MSVC runtime and linking - // libc++ would conflict with it. + // libcpp is required for various dependencies. On MSVC, we must + // not use linkLibCpp because Zig unconditionally passes -nostdinc++ + // and then adds its bundled libc++/libc++abi include paths, which + // conflict with MSVC's own C++ runtime headers. The MSVC SDK + // include directories (already added via linkLibC above) contain + // both C and C++ headers, so linkLibCpp is not needed. if (step.rootModuleTarget().abi != .msvc) { step.linkLibCpp(); }