mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-31 20:59:02 +00:00
Xcode 27's math.h uses the __need_infinity_nan protocol provided by matching Clang resource headers. Zig 0.16's bundled float.h predates that protocol, causing the bundled libc++ compilation to fail. Overlay the SDK math.h through the Apple SDK libc include path and provide the missing infinity and NaN definitions. The compatibility header can be removed once Zig's bundled Clang headers support the protocol.
206 lines
8.0 KiB
Zig
206 lines
8.0 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
// The cache. This always uses b.allocator and never frees memory
|
|
// (which is idiomatic for a Zig build exe). We cache the libc txt
|
|
// file we create because it is expensive to generate (subprocesses).
|
|
pub const Cache = struct {
|
|
const Key = struct {
|
|
arch: std.Target.Cpu.Arch,
|
|
os: std.Target.Os.Tag,
|
|
abi: std.Target.Abi,
|
|
};
|
|
|
|
pub const Value = union(enum) {
|
|
native: struct {
|
|
libc: std.Build.LazyPath,
|
|
framework: []const u8,
|
|
system_include: []const u8,
|
|
library: []const u8,
|
|
},
|
|
cross: struct {
|
|
libc: std.Build.LazyPath,
|
|
},
|
|
};
|
|
|
|
var map: std.AutoHashMapUnmanaged(Key, ?Value) = .{};
|
|
};
|
|
|
|
pub fn build(b: *std.Build) !void {
|
|
const target = b.standardTargetOptions(.{});
|
|
const optimize = b.standardOptimizeOption(.{});
|
|
_ = target;
|
|
_ = optimize;
|
|
}
|
|
|
|
/// Fetch or load the paths for the proper Apple SDK for libc and
|
|
/// frameworks. When running on a Darwin host, this uses the native
|
|
/// SDK installed on the system via `xcrun`. When cross-compiling from
|
|
/// a non-Darwin host, it falls back to Zig's bundled Darwin headers.
|
|
pub fn pathsForTarget(b: *std.Build, target: std.Target) !Cache.Value {
|
|
const gop = try Cache.map.getOrPut(b.allocator, .{
|
|
.arch = target.cpu.arch,
|
|
.os = target.os.tag,
|
|
.abi = target.abi,
|
|
});
|
|
|
|
if (!gop.found_existing) init: {
|
|
if (comptime builtin.os.tag.isDarwin()) darwin: {
|
|
// Detect our SDK using the "findNative" Zig stdlib function.
|
|
// This is really important because it forces using `xcrun` to
|
|
// find the SDK path.
|
|
var libc = std.zig.LibCInstallation.findNative(
|
|
b.allocator,
|
|
b.graph.io,
|
|
.{
|
|
.environ_map = &b.graph.environ_map,
|
|
.target = &target,
|
|
.verbose = false,
|
|
},
|
|
) catch break :darwin;
|
|
|
|
// Xcode 27's math.h requests infinity and NaN definitions from
|
|
// Clang's float.h using the __need_infinity_nan protocol. Zig
|
|
// 0.16's bundled Clang resource headers predate that protocol, so
|
|
// compiling Zig's bundled libc++ against the new SDK fails.
|
|
//
|
|
// Put our compatibility include directory between Zig's resource
|
|
// headers and the selected SDK headers. Its math.h forwards to the
|
|
// SDK with #include_next, then supplies the definitions missing
|
|
// from Zig's float.h. This can be removed once Zig's bundled Clang
|
|
// headers implement __need_infinity_nan.
|
|
libc.include_dir = b.dependency("apple_sdk", .{})
|
|
.path("include")
|
|
.getPath(b);
|
|
|
|
// Render the file compatible with the `--libc` Zig flag.
|
|
var stream: std.Io.Writer.Allocating = .init(b.allocator);
|
|
defer stream.deinit();
|
|
try libc.render(&stream.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", stream.written());
|
|
|
|
// 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.* = .{ .native = .{
|
|
.libc = path,
|
|
.framework = framework_path,
|
|
.system_include = libc.sys_include_dir.?,
|
|
.library = library_path,
|
|
} };
|
|
|
|
break :init;
|
|
}
|
|
|
|
// Cross-compiling to Darwin from a non-Darwin host.
|
|
// Zig only bundles macOS headers, so for other Apple platforms
|
|
// we leave the value as null to produce a descriptive error.
|
|
if (target.os.tag != .macos) {
|
|
gop.value_ptr.* = null;
|
|
break :init;
|
|
}
|
|
|
|
// Fall back to Zig's bundled Darwin headers for libc resolution.
|
|
const zig_lib_path = b.graph.zig_lib_directory.path.?;
|
|
const include_dir = b.pathJoin(&.{
|
|
zig_lib_path, "libc", "include", "any-darwin-any",
|
|
});
|
|
|
|
const wf = b.addWriteFiles();
|
|
const path = wf.add("libc.txt", b.fmt(
|
|
\\include_dir={s}
|
|
\\sys_include_dir={s}
|
|
\\crt_dir=
|
|
\\msvc_lib_dir=
|
|
\\kernel32_lib_dir=
|
|
\\gcc_dir=
|
|
\\
|
|
, .{ include_dir, include_dir }));
|
|
|
|
gop.value_ptr.* = .{ .cross = .{ .libc = path } };
|
|
}
|
|
|
|
return 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.
|
|
.macos => error.XcodeMacOSSDKNotFound,
|
|
.ios => error.XcodeiOSSDKNotFound,
|
|
.tvos => error.XcodeTVOSSDKNotFound,
|
|
.watchos => error.XcodeWatchOSSDKNotFound,
|
|
else => error.XcodeAppleSDKNotFound,
|
|
};
|
|
}
|
|
|
|
/// Setup the step to point to the proper Apple SDK for libc and
|
|
/// frameworks. When running on a Darwin host, this uses the native
|
|
/// SDK installed on the system via `xcrun`. When cross-compiling from
|
|
/// a non-Darwin host, it falls back to Zig's bundled Darwin headers.
|
|
pub fn addPaths(
|
|
b: *std.Build,
|
|
step: *std.Build.Step.Compile,
|
|
) !void {
|
|
const target = step.rootModuleTarget();
|
|
|
|
// A three paragraph comment to describe a single C macro. Strap in.
|
|
//
|
|
// Zig uses its own libc++ headers when compiling C++. In Zig 0.16,
|
|
// `__config` skips `__config_site`, the file that normally contains
|
|
// platform settings, and expects those settings as `-D` flags:
|
|
// https://codeberg.org/ziglang/zig/src/tag/0.16.0/lib/libcxx/include/__config#L13
|
|
//
|
|
// This macro enables Apple's availability checks. Without them, libc++
|
|
// assumes every symbol declared by these newer headers also exists in the
|
|
// target's runtime library:
|
|
// https://github.com/llvm/llvm-project/blob/llvmorg-21.1.0/libcxx/include/__configuration/availability.h#L63-L125
|
|
//
|
|
// For example, `hash.h` either calls `std::__hash_memory` from the runtime
|
|
// library or compiles an inline fallback:
|
|
// https://github.com/llvm/llvm-project/blob/llvmorg-21.1.0/libcxx/include/__functional/hash.h#L244-L250
|
|
// Without the checks, it chooses the runtime symbol. Older macOS versions
|
|
// don't have that symbol, so dyld aborts at launch:
|
|
// https://github.com/llvm/llvm-project/issues/155531
|
|
step.root_module.addCMacro(
|
|
"_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS",
|
|
"1",
|
|
);
|
|
|
|
switch (try pathsForTarget(b, target)) {
|
|
.native => |native| {
|
|
step.setLibCFile(native.libc);
|
|
|
|
// This is only necessary until this bug is fixed:
|
|
// https://github.com/ziglang/zig/issues/24024
|
|
step.root_module.addSystemFrameworkPath(.{ .cwd_relative = native.framework });
|
|
step.root_module.addSystemIncludePath(.{ .cwd_relative = native.system_include });
|
|
step.root_module.addLibraryPath(.{ .cwd_relative = native.library });
|
|
},
|
|
.cross => |cross| {
|
|
step.setLibCFile(cross.libc);
|
|
},
|
|
}
|
|
}
|