mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-19 22:10:29 +00:00
build: fix C++ linking and enum signedness on MSVC (#11812)
> [!WARNING] > Review/approve this AFTER #11807 and #11810 (this PR includes their commits) ## Summary ### **And `run test ghostty-test` finally runs on Windows! 🎉almost there!** - Skip `linkLibCpp()` on MSVC for dcimgui, spirv-cross, and harfbuzz (same fix already applied upstream to highway, simdutf, utfcpp, glslang, SharedDeps, GhosttyZig) - Fix freetype C enum signedness: MSVC translates C enums as signed `int`, while GCC/Clang uses unsigned `int`. Add `@intCast` at call sites and `@bitCast` for bit-shift operations on glyph format tags. ## Context Zig unconditionally passes `-nostdinc++` and adds its bundled libc++/libc++abi include paths, which conflict with MSVC's own C++ runtime headers. The MSVC SDK directories (added via `linkLibC`) already contain both C and C++ headers, so `linkLibCpp` is not needed. The freetype enum issue is a different facet of the same MSVC vs GCC/Clang divide: `FT_Render_Mode` and `FT_Glyph_Format` are C enums that get different signedness on different compilers. ## Stack Stacked on 015-windows/fix-ssize-t-msvc. ## Test plan ### Cross-platform results (`zig build test` / `zig build -Dapp-runtime=none test` on Windows) | | Windows | Linux | Mac | |---|---|---|---| | **BEFORE** (015,a35f84db3) | FAIL - 48/51, 1 failed (compile ghostty-test) | PASS - 86/86, 2655/2678, 23 skipped | PASS - 160/160, 2655/2662, 7 skipped | | **AFTER** (016,ce9930051) | FAIL - 49/51, 2630/2654 tests passed, 1 failed, 23 skipped | PASS - 86/86, 2655/2678, 23 skipped | PASS - 160/160, 2655/2662, 7 skipped | ### Windows: what changed (48 -> 49 steps, tests now run) **Fixed by this PR:** - `compile test ghostty-test` - was `3 errors` (libcxxabi conflicts + freetype type mismatches) -> `success` - `run test ghostty-test` - now actually runs: 2630 passed, 23 skipped, 1 failed **Remaining test failure (pre-existing, unrelated):** - `ghostty.h MouseShape` - `checkGhosttyHEnum` cannot find `GHOSTTY_MOUSE_SHAPE_*` constants in the translate-c output. This is a translate-c issue with how MSVC enum constants are exposed, not related to C++ linking or enum signedness. ### Linux/macOS: no regressions Identical pass counts and test results before and after. ## Discussion ### Grep wider: other unconditional linkLibCpp calls `pkg/breakpad/build.zig` still calls `linkLibCpp()` unconditionally but is behind sentry and not in the Windows build path. Noted for completeness. ### Freetype enum signedness The freetype Zig bindings define `RenderMode = enum(c_uint)` and `Encoding = enum(u31)`. On MSVC, C enums are `int` (signed), so the translated C functions expect `c_int` parameters. The fix adds `@intCast` to convert between signed and unsigned at call sites. This is safe because the enum values are small positive integers that fit in both types. Also, not sure if there's a better way to make this change more elegantly. The comments are replicated in each instance, probably overkill but I have seen this same pattern elsewhere in the codebase. ## What I Learnt - More of the same
This commit is contained in:
@@ -26,7 +26,14 @@ pub fn build(b: *std.Build) !void {
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibC();
|
||||
lib.linkLibCpp();
|
||||
// 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();
|
||||
}
|
||||
b.installArtifact(lib);
|
||||
|
||||
// Zig module
|
||||
|
||||
@@ -52,7 +52,7 @@ pub const Face = struct {
|
||||
|
||||
/// Select a given charmap by its encoding tag (as listed in freetype.h).
|
||||
pub fn selectCharmap(self: Face, encoding: Encoding) Error!void {
|
||||
return intToError(c.FT_Select_Charmap(self.handle, @intFromEnum(encoding)));
|
||||
return intToError(c.FT_Select_Charmap(self.handle, @intCast(@intFromEnum(encoding))));
|
||||
}
|
||||
|
||||
/// Call FT_Request_Size to request the nominal size (in points).
|
||||
@@ -99,7 +99,7 @@ pub const Face = struct {
|
||||
pub fn renderGlyph(self: Face, render_mode: RenderMode) Error!void {
|
||||
return intToError(c.FT_Render_Glyph(
|
||||
self.handle.*.glyph,
|
||||
@intFromEnum(render_mode),
|
||||
@intCast(@intFromEnum(render_mode)),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,14 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibC();
|
||||
lib.linkLibCpp();
|
||||
// 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();
|
||||
}
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
|
||||
@@ -58,7 +58,14 @@ fn buildSpirvCross(
|
||||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibC();
|
||||
lib.linkLibCpp();
|
||||
// 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();
|
||||
}
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
|
||||
@@ -679,13 +679,16 @@ pub const Face = struct {
|
||||
else => |f| {
|
||||
// Glyph formats are tags, so we can
|
||||
// output a semi-readable error here.
|
||||
// Use @bitCast to u32 because MSVC translates C enums
|
||||
// as signed int, while GCC/Clang uses unsigned int.
|
||||
const tag: u32 = @bitCast(f);
|
||||
log.err(
|
||||
"Can't render glyph with unsupported glyph format \"{s}\"",
|
||||
.{[4]u8{
|
||||
@truncate(f >> 24),
|
||||
@truncate(f >> 16),
|
||||
@truncate(f >> 8),
|
||||
@truncate(f >> 0),
|
||||
@truncate(tag >> 24),
|
||||
@truncate(tag >> 16),
|
||||
@truncate(tag >> 8),
|
||||
@truncate(tag >> 0),
|
||||
}},
|
||||
);
|
||||
return error.UnsupportedGlyphFormat;
|
||||
|
||||
Reference in New Issue
Block a user