From 0f1860f066cff0f4f93fa8d7bbe161cda3f3cb98 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 29 May 2025 14:47:29 -0700 Subject: [PATCH] build: use a libc txt file to point to correct Apple SDK This fixes an issue where Ghostty would not build against the macOS 15.5 SDK. What was happening was that Zig was adding its embedded libc paths to the clang command line, which included old headers that were incompatible with the latest (macOS 15.5) SDK. Ghostty was adding the newer paths but they were being overridden by the embedded libc paths. The reason this was happening is because Zig was using its own logic to find the libc paths and this was colliding with the paths we were setting manually. To fix this, we now use a `libc.txt` file that explicitly tells Zig where to find libc, and we base this on our own SDK search logic. --- pkg/apple-sdk/build.zig | 91 +++++++++++++++++++++++++++++++-------- pkg/breakpad/build.zig | 2 +- pkg/cimgui/build.zig | 3 +- pkg/freetype/build.zig | 2 +- pkg/glfw/build.zig | 5 +-- pkg/glslang/build.zig | 6 +-- pkg/harfbuzz/build.zig | 3 +- pkg/highway/build.zig | 3 +- pkg/libintl/build.zig | 2 +- pkg/libpng/build.zig | 2 +- pkg/macos/build.zig | 5 +-- pkg/oniguruma/build.zig | 2 +- pkg/sentry/build.zig | 3 +- pkg/simdutf/build.zig | 2 +- pkg/spirv-cross/build.zig | 2 +- pkg/utfcpp/build.zig | 2 +- pkg/wuffs/build.zig | 5 --- pkg/zlib/build.zig | 2 +- src/build/SharedDeps.zig | 2 +- 19 files changed, 92 insertions(+), 52 deletions(-) diff --git a/pkg/apple-sdk/build.zig b/pkg/apple-sdk/build.zig index 1be733dd6..18a6c0968 100644 --- a/pkg/apple-sdk/build.zig +++ b/pkg/apple-sdk/build.zig @@ -7,12 +7,17 @@ pub fn build(b: *std.Build) !void { _ = optimize; } -/// Add the SDK framework, include, and library paths to the given module. -/// The module target is used to determine the SDK to use so it must have -/// a resolved target. -pub fn addPaths(b: *std.Build, m: *std.Build.Module) !void { +/// Setup the step to point to the proper Apple SDK for libc and +/// frameworks. This expects and relies on the native SDK being +/// installed on the system. Ghostty doesn't support cross-compilation +/// for Apple platforms. +pub fn addPaths( + b: *std.Build, + step: *std.Build.Step.Compile, +) !void { // The cache. This always uses b.allocator and never frees memory - // (which is idiomatic for a Zig build exe). + // (which is idiomatic for a Zig build exe). We cache the libc txt + // file we create because it is expensive to generate (subprocesses). const Cache = struct { const Key = struct { arch: std.Target.Cpu.Arch, @@ -20,27 +25,72 @@ pub fn addPaths(b: *std.Build, m: *std.Build.Module) !void { abi: std.Target.Abi, }; - var map: std.AutoHashMapUnmanaged(Key, ?[]const u8) = .{}; + var map: std.AutoHashMapUnmanaged(Key, ?struct { + libc: std.Build.LazyPath, + framework: []const u8, + system_include: []const u8, + library: []const u8, + }) = .{}; }; - const target = m.resolved_target.?.result; + const target = step.rootModuleTarget(); const gop = try Cache.map.getOrPut(b.allocator, .{ .arch = target.cpu.arch, .os = target.os.tag, .abi = target.abi, }); - // This executes `xcrun` to get the SDK path. We don't want to execute - // this multiple times so we cache the value. if (!gop.found_existing) { - gop.value_ptr.* = std.zig.system.darwin.getSdk( - b.allocator, - m.resolved_target.?.result, - ); + // Detect our SDK using the "findNative" Zig stdlib function. + // This is really important because it forces using `xcrun` to + // find the SDK path. + const libc = try std.zig.LibCInstallation.findNative(.{ + .allocator = b.allocator, + .target = step.rootModuleTarget(), + .verbose = false, + }); + + // Render the file compatible with the `--libc` Zig flag. + var list: std.ArrayList(u8) = .init(b.allocator); + defer list.deinit(); + try libc.render(list.writer()); + + // Create a temporary file to store the libc path because + // `--libc` expects a file path. + const wf = b.addWriteFiles(); + const path = wf.add("libc.txt", list.items); + + // Determine our framework path. Zig has a bug where it doesn't + // parse this from the libc txt file for `-framework` flags: + // https://github.com/ziglang/zig/issues/24024 + const framework_path = framework: { + const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?; + const down2 = std.fs.path.dirname(down1).?; + break :framework try std.fs.path.join(b.allocator, &.{ + down2, + "System", + "Library", + "Frameworks", + }); + }; + + const library_path = library: { + const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?; + break :library try std.fs.path.join(b.allocator, &.{ + down1, + "lib", + }); + }; + + gop.value_ptr.* = .{ + .libc = path, + .framework = framework_path, + .system_include = libc.sys_include_dir.?, + .library = library_path, + }; } - // The active SDK we want to use - const path = gop.value_ptr.* orelse return switch (target.os.tag) { + const value = gop.value_ptr.* orelse return switch (target.os.tag) { // Return a more descriptive error. Before we just returned the // generic error but this was confusing a lot of community members. // It costs us nothing in the build script to return something better. @@ -50,7 +100,12 @@ pub fn addPaths(b: *std.Build, m: *std.Build.Module) !void { .watchos => error.XcodeWatchOSSDKNotFound, else => error.XcodeAppleSDKNotFound, }; - m.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ path, "/System/Library/Frameworks" }) }); - m.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ path, "/usr/include" }) }); - m.addLibraryPath(.{ .cwd_relative = b.pathJoin(&.{ path, "/usr/lib" }) }); + + step.setLibCFile(value.libc); + + // This is only necessary until this bug is fixed: + // https://github.com/ziglang/zig/issues/24024 + step.root_module.addSystemFrameworkPath(.{ .cwd_relative = value.framework }); + step.root_module.addSystemIncludePath(.{ .cwd_relative = value.system_include }); + step.root_module.addLibraryPath(.{ .cwd_relative = value.library }); } diff --git a/pkg/breakpad/build.zig b/pkg/breakpad/build.zig index e2fdec7ad..42247b12c 100644 --- a/pkg/breakpad/build.zig +++ b/pkg/breakpad/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) !void { lib.addIncludePath(b.path("vendor")); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/cimgui/build.zig b/pkg/cimgui/build.zig index c76b53966..3ca735383 100644 --- a/pkg/cimgui/build.zig +++ b/pkg/cimgui/build.zig @@ -84,8 +84,7 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag.isDarwin()) { if (!target.query.isNative()) { - try @import("apple_sdk").addPaths(b, lib.root_module); - try @import("apple_sdk").addPaths(b, module); + try @import("apple_sdk").addPaths(b, lib); } lib.addCSourceFile(.{ .file = imgui.path("backends/imgui_impl_metal.mm"), diff --git a/pkg/freetype/build.zig b/pkg/freetype/build.zig index bfe27e5aa..e9f72210a 100644 --- a/pkg/freetype/build.zig +++ b/pkg/freetype/build.zig @@ -69,7 +69,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/glfw/build.zig b/pkg/glfw/build.zig index cc61f18b2..142a558da 100644 --- a/pkg/glfw/build.zig +++ b/pkg/glfw/build.zig @@ -24,7 +24,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }); if (target.result.os.tag.isDarwin()) { - try apple_sdk.addPaths(b, exe.root_module); + try apple_sdk.addPaths(b, exe); } const tests_run = b.addRunArtifact(exe); @@ -122,8 +122,7 @@ fn buildLib( }, .macos => { - try apple_sdk.addPaths(b, lib.root_module); - try apple_sdk.addPaths(b, module); + try apple_sdk.addPaths(b, lib); // Transitive dependencies, explicit linkage of these works around // ziglang/zig#17130 diff --git a/pkg/glslang/build.zig b/pkg/glslang/build.zig index 629490aa4..747216a39 100644 --- a/pkg/glslang/build.zig +++ b/pkg/glslang/build.zig @@ -16,10 +16,6 @@ pub fn build(b: *std.Build) !void { module.addIncludePath(upstream.path("")); module.addIncludePath(b.path("override")); - if (target.result.os.tag.isDarwin()) { - const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, module); - } if (target.query.isNative()) { const test_exe = b.addTest(.{ @@ -55,7 +51,7 @@ fn buildGlslang( lib.addIncludePath(b.path("override")); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/harfbuzz/build.zig b/pkg/harfbuzz/build.zig index d0dd6d01c..3bdc30a32 100644 --- a/pkg/harfbuzz/build.zig +++ b/pkg/harfbuzz/build.zig @@ -93,8 +93,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu lib.linkLibCpp(); if (target.result.os.tag.isDarwin()) { - try apple_sdk.addPaths(b, lib.root_module); - try apple_sdk.addPaths(b, module); + try apple_sdk.addPaths(b, lib); } const dynamic_link_opts = options.dynamic_link_opts; diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index c72ca355f..5036316da 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -23,8 +23,7 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); - try apple_sdk.addPaths(b, module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/libintl/build.zig b/pkg/libintl/build.zig index 53eb67f16..1baed195a 100644 --- a/pkg/libintl/build.zig +++ b/pkg/libintl/build.zig @@ -40,7 +40,7 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } if (b.lazyDependency("gettext", .{})) |upstream| { diff --git a/pkg/libpng/build.zig b/pkg/libpng/build.zig index d012f2712..8729398f8 100644 --- a/pkg/libpng/build.zig +++ b/pkg/libpng/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) !void { } if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } // For dynamic linking, we prefer dynamic linking and to search by diff --git a/pkg/macos/build.zig b/pkg/macos/build.zig index 911664a2f..df76da9b4 100644 --- a/pkg/macos/build.zig +++ b/pkg/macos/build.zig @@ -45,8 +45,7 @@ pub fn build(b: *std.Build) !void { module.linkFramework("CoreVideo", .{}); module.linkFramework("QuartzCore", .{}); - try apple_sdk.addPaths(b, lib.root_module); - try apple_sdk.addPaths(b, module); + try apple_sdk.addPaths(b, lib); } b.installArtifact(lib); @@ -58,7 +57,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }); if (target.result.os.tag.isDarwin()) { - try apple_sdk.addPaths(b, test_exe.root_module); + try apple_sdk.addPaths(b, test_exe); } test_exe.linkLibrary(lib); diff --git a/pkg/oniguruma/build.zig b/pkg/oniguruma/build.zig index 1c93bbf9a..c23d744df 100644 --- a/pkg/oniguruma/build.zig +++ b/pkg/oniguruma/build.zig @@ -67,7 +67,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } if (b.lazyDependency("oniguruma", .{})) |upstream| { diff --git a/pkg/sentry/build.zig b/pkg/sentry/build.zig index 3c0019710..0e6993ad4 100644 --- a/pkg/sentry/build.zig +++ b/pkg/sentry/build.zig @@ -20,8 +20,7 @@ pub fn build(b: *std.Build) !void { lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); - try apple_sdk.addPaths(b, module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index 859653443..30de40fea 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/spirv-cross/build.zig b/pkg/spirv-cross/build.zig index c7d0d2039..ff67e3e72 100644 --- a/pkg/spirv-cross/build.zig +++ b/pkg/spirv-cross/build.zig @@ -44,7 +44,7 @@ fn buildSpirvCross( lib.linkLibCpp(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/utfcpp/build.zig b/pkg/utfcpp/build.zig index 6b80fec7b..8e1a3cb20 100644 --- a/pkg/utfcpp/build.zig +++ b/pkg/utfcpp/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) !void { if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } var flags = std.ArrayList([]const u8).init(b.allocator); diff --git a/pkg/wuffs/build.zig b/pkg/wuffs/build.zig index d47771c22..4d144e76a 100644 --- a/pkg/wuffs/build.zig +++ b/pkg/wuffs/build.zig @@ -11,11 +11,6 @@ pub fn build(b: *std.Build) !void { .link_libc = true, }); - if (target.result.os.tag.isDarwin()) { - const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, module); - } - const unit_tests = b.addTest(.{ .root_source_file = b.path("src/main.zig"), .target = target, diff --git a/pkg/zlib/build.zig b/pkg/zlib/build.zig index 28ae62424..28344c989 100644 --- a/pkg/zlib/build.zig +++ b/pkg/zlib/build.zig @@ -12,7 +12,7 @@ pub fn build(b: *std.Build) !void { lib.linkLibC(); if (target.result.os.tag.isDarwin()) { const apple_sdk = @import("apple_sdk"); - try apple_sdk.addPaths(b, lib.root_module); + try apple_sdk.addPaths(b, lib); } if (b.lazyDependency("zlib", .{})) |upstream| { diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index 512975ac0..d3741a358 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -377,7 +377,7 @@ pub fn add( // We always require the system SDK so that our system headers are available. // This makes things like `os/log.h` available for cross-compiling. if (step.rootModuleTarget().os.tag.isDarwin()) { - try @import("apple_sdk").addPaths(b, step.root_module); + try @import("apple_sdk").addPaths(b, step); const metallib = self.metallib.?; metallib.output.addStepDependencies(&step.step);