build: improve caching of macOS builds

This improves caching of builds to speed up `zig build` and
`zig build run` on macOS by avoiding unnecessary rebuilds of the Ghostty
xcframework.
This commit is contained in:
Mitchell Hashimoto
2025-07-07 07:18:58 -07:00
parent 292d89dbe2
commit b78434e741
2 changed files with 38 additions and 22 deletions

View File

@ -1,12 +1,15 @@
const GhosttyXCFramework = @This(); const GhosttyXCFramework = @This();
const std = @import("std"); const std = @import("std");
const RunStep = std.Build.Step.Run;
const Config = @import("Config.zig"); const Config = @import("Config.zig");
const SharedDeps = @import("SharedDeps.zig"); const SharedDeps = @import("SharedDeps.zig");
const GhosttyLib = @import("GhosttyLib.zig"); const GhosttyLib = @import("GhosttyLib.zig");
const XCFrameworkStep = @import("XCFrameworkStep.zig"); const XCFrameworkStep = @import("XCFrameworkStep.zig");
xcframework: *XCFrameworkStep, xcframework: *XCFrameworkStep,
copy: *std.Build.Step,
target: Target, target: Target,
pub const Target = enum { native, universal }; pub const Target = enum { native, universal };
@ -58,7 +61,6 @@ pub fn init(
// it to the final app built with Swift. // it to the final app built with Swift.
const xcframework = XCFrameworkStep.create(b, .{ const xcframework = XCFrameworkStep.create(b, .{
.name = "GhosttyKit", .name = "GhosttyKit",
.out_path = "macos/GhosttyKit.xcframework",
.libraries = switch (target) { .libraries = switch (target) {
.universal => &.{ .universal => &.{
.{ .{
@ -82,8 +84,32 @@ pub fn init(
}, },
}); });
// A command to copy the xcframework to the output directory,
// because the xcode project needs a stable path.
const copy = copy: {
const remove = RunStep.create(b, "remove old xcframework");
remove.has_side_effects = true;
remove.cwd = b.path("");
remove.addArgs(&.{
"rm",
"-rf",
"macos/GhosttyKit.xcframework",
});
remove.expectExitCode(0);
const step = RunStep.create(b, "copy xcframework");
step.has_side_effects = true;
step.cwd = b.path("");
step.addArgs(&.{ "cp", "-R" });
step.addDirectoryArg(xcframework.output);
step.addArg("macos/GhosttyKit.xcframework");
step.step.dependOn(&remove.step);
break :copy step;
};
return .{ return .{
.xcframework = xcframework, .xcframework = xcframework,
.copy = &copy.step,
.target = target, .target = target,
}; };
} }
@ -97,5 +123,5 @@ pub fn addStepDependencies(
self: *const GhosttyXCFramework, self: *const GhosttyXCFramework,
other_step: *std.Build.Step, other_step: *std.Build.Step,
) void { ) void {
other_step.dependOn(self.xcframework.step); other_step.dependOn(self.copy);
} }

View File

@ -12,9 +12,6 @@ pub const Options = struct {
/// The name of the xcframework to create. /// The name of the xcframework to create.
name: []const u8, name: []const u8,
/// The path to write the framework
out_path: []const u8,
/// The libraries to bundle /// The libraries to bundle
libraries: []const Library, libraries: []const Library,
}; };
@ -30,40 +27,33 @@ pub const Library = struct {
step: *Step, step: *Step,
output: LazyPath,
pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep { pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep {
const self = b.allocator.create(XCFrameworkStep) catch @panic("OOM"); const self = b.allocator.create(XCFrameworkStep) catch @panic("OOM");
// We have to delete the old xcframework first since we're writing
// to a static path.
const run_delete = run: {
const run = RunStep.create(b, b.fmt("xcframework delete {s}", .{opts.name}));
run.has_side_effects = true;
run.addArgs(&.{ "rm", "-rf", opts.out_path });
break :run run;
};
// Then we run xcodebuild to create the framework. // Then we run xcodebuild to create the framework.
const run_create = run: { const run_create, const run_output = run: {
const run = RunStep.create(b, b.fmt("xcframework {s}", .{opts.name})); const run = RunStep.create(b, b.fmt("xcframework {s}", .{opts.name}));
run.has_side_effects = true;
run.addArgs(&.{ "xcodebuild", "-create-xcframework" }); run.addArgs(&.{ "xcodebuild", "-create-xcframework" });
for (opts.libraries) |lib| { for (opts.libraries) |lib| {
run.addArg("-library"); run.addArg("-library");
run.addFileArg(lib.library); run.addFileArg(lib.library);
run.addArg("-headers"); run.addArg("-headers");
run.addFileArg(lib.headers); run.addDirectoryArg(lib.headers);
} }
run.addArg("-output"); run.addArg("-output");
run.addArg(opts.out_path); const output = run.addOutputDirectoryArg(b.fmt(
"{s}.xcframework",
.{opts.name},
));
run.expectExitCode(0); run.expectExitCode(0);
_ = run.captureStdOut(); break :run .{ run, output };
_ = run.captureStdErr();
break :run run;
}; };
run_create.step.dependOn(&run_delete.step);
self.* = .{ self.* = .{
.step = &run_create.step, .step = &run_create.step,
.output = run_output,
}; };
return self; return self;