ghostty/src/build/MetallibStep.zig
Mitchell Hashimoto b0e0aadaf3 build: Xcode 26, macOS Tahoe support (build tooling only)
This updates our build script and CI to support Xcode 26 and macOS
Tahoe. **This doesn't update the Ghostty app to resolve any Tahoe
issues.**

For CI, we've added a new build job that runs on macOS Tahoe with Xcode
26. I've stopped short of updating our tip release job, since I think I
want to wait until I verify a bit more about Tahoe before we flip that
bit. Also, ideally, we'd run Xcode 26 on Sequoia (macOS 15) for
stability reasons and Namespace doesn't have Xcode 26 on 15 yet.

For builds, this updates our build script to find Metal binaries using
`xcodebuild -find-executable` instead of `xcrun`. The latter doesn't
work with Xcode 26, but the former does and also still works with older
Xcodes. I'm not sure if this is a bug but I did report it: FB17874042.
2025-06-10 07:10:40 -07:00

96 lines
2.8 KiB
Zig

/// A zig build step that compiles a set of ".metal" files into a
/// ".metallib" file.
const MetallibStep = @This();
const std = @import("std");
const Step = std.Build.Step;
const RunStep = std.Build.Step.Run;
const LazyPath = std.Build.LazyPath;
pub const Options = struct {
/// The name of the xcframework to create.
name: []const u8,
/// The OS being targeted
target: std.Build.ResolvedTarget,
/// The Metal source files.
sources: []const LazyPath,
};
step: *Step,
output: LazyPath,
pub fn create(b: *std.Build, opts: Options) ?*MetallibStep {
switch (opts.target.result.os.tag) {
.macos, .ios => {},
else => return null, // Only macOS and iOS are supported.
}
const self = b.allocator.create(MetallibStep) catch @panic("OOM");
const min_version = if (opts.target.query.os_version_min) |v|
b.fmt("{}", .{v.semver})
else switch (opts.target.result.os.tag) {
.macos => "10.14",
.ios => "11.0",
else => unreachable,
};
// Find the metal and metallib executables. The Apple docs
// at the time of writing (June 2025) say to use
// `xcrun --sdk <sdk> metal` but this doesn't work with Xcode 26.
//
// I don't know if this is a bug but the xcodebuild approach also
// works with Xcode 15 so it seems safe to use this instead.
//
// Reported bug: FB17874042.
var code: u8 = undefined;
const metal_exe = std.mem.trim(u8, b.runAllowFail(
&.{ "xcodebuild", "-find-executable", "metal" },
&code,
.Ignore,
) catch return null, "\r\n ");
const metallib_exe = std.mem.trim(u8, b.runAllowFail(
&.{ "xcodebuild", "-find-executable", "metallib" },
&code,
.Ignore,
) catch return null, "\r\n ");
const run_ir = RunStep.create(
b,
b.fmt("metal {s}", .{opts.name}),
);
run_ir.addArgs(&.{ metal_exe, "-o" });
const output_ir = run_ir.addOutputFileArg(b.fmt("{s}.ir", .{opts.name}));
run_ir.addArgs(&.{"-c"});
for (opts.sources) |source| run_ir.addFileArg(source);
switch (opts.target.result.os.tag) {
.ios => run_ir.addArgs(&.{b.fmt(
"-mios-version-min={s}",
.{min_version},
)}),
.macos => run_ir.addArgs(&.{b.fmt(
"-mmacos-version-min={s}",
.{min_version},
)}),
else => {},
}
const run_lib = RunStep.create(
b,
b.fmt("metallib {s}", .{opts.name}),
);
run_lib.addArgs(&.{ metallib_exe, "-o" });
const output_lib = run_lib.addOutputFileArg(b.fmt("{s}.metallib", .{opts.name}));
run_lib.addFileArg(output_ir);
run_lib.step.dependOn(&run_ir.step);
self.* = .{
.step = &run_lib.step,
.output = output_lib,
};
return self;
}