From 1abaf87de951282f3316c1e570501c0e595329c2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 14 Feb 2023 11:11:34 -0800 Subject: [PATCH] build: LipoStep --- build.zig | 42 +++++++++++++++++++---- macos/.gitignore | 1 + src/build/LipoStep.zig | 75 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 src/build/LipoStep.zig diff --git a/build.zig b/build.zig index e170dcb5b..539a0f148 100644 --- a/build.zig +++ b/build.zig @@ -21,6 +21,7 @@ const zlib = @import("pkg/zlib/build.zig"); const tracylib = @import("pkg/tracy/build.zig"); const system_sdk = @import("vendor/mach/libs/glfw/system_sdk.zig"); const WasmTarget = @import("src/os/wasm/target.zig").Target; +const LipoStep = @import("src/build/LipoStep.zig"); const XCFrameworkStep = @import("src/build/XCFrameworkStep.zig"); // Do a comptime Zig version requirement. The required Zig version is @@ -133,28 +134,57 @@ pub fn build(b: *std.build.Builder) !void { } // c lib - const static_lib = lib: { + { const static_lib = b.addStaticLibrary("ghostty", "src/main_c.zig"); static_lib.setBuildMode(mode); static_lib.setTarget(target); static_lib.install(); static_lib.linkLibC(); b.default_step.dependOn(&static_lib.step); - break :lib static_lib; - }; + } // On Mac we can build the app. - const macapp = b.step("macapp", "Build macOS app"); + const macapp = b.step("macapp", "Build macOS app using XCode."); if (builtin.target.isDarwin()) { + const static_lib_aarch64 = lib: { + const lib = b.addStaticLibrary("ghostty", "src/main_c.zig"); + lib.setBuildMode(mode); + lib.setTarget(try std.zig.CrossTarget.parse(.{ .arch_os_abi = "aarch64-macos" })); + lib.install(); + lib.linkLibC(); + b.default_step.dependOn(&lib.step); + break :lib lib; + }; + + const static_lib_x86_64 = lib: { + const lib = b.addStaticLibrary("ghostty", "src/main_c.zig"); + lib.setBuildMode(mode); + lib.setTarget(try std.zig.CrossTarget.parse(.{ .arch_os_abi = "x86_64-macos" })); + lib.install(); + lib.linkLibC(); + b.default_step.dependOn(&lib.step); + break :lib lib; + }; + + const static_lib_universal = LipoStep.create(b, .{ + .name = "ghostty", + .out_name = "libghostty.a", + .input_a = .{ .generated = &static_lib_aarch64.output_path_source }, + .input_b = .{ .generated = &static_lib_x86_64.output_path_source }, + }); + static_lib_universal.step.dependOn(&static_lib_aarch64.step); + static_lib_universal.step.dependOn(&static_lib_x86_64.step); + // The xcframework wraps our ghostty library so that we can link // it to the final app built with Swift. const xcframework = XCFrameworkStep.create(b, .{ .name = "GhosttyKit", .out_path = "macos/GhosttyKit.xcframework", - .library = .{ .generated = &static_lib.output_lib_path_source }, + .library = .{ .generated = &static_lib_universal.out_path }, .headers = .{ .path = "include" }, }); - xcframework.step.dependOn(&static_lib.step); + xcframework.step.dependOn(&static_lib_universal.step); + b.default_step.dependOn(&xcframework.step); macapp.dependOn(&xcframework.step); } diff --git a/macos/.gitignore b/macos/.gitignore index 246f7e8b5..07895edd7 100644 --- a/macos/.gitignore +++ b/macos/.gitignore @@ -1,4 +1,5 @@ .DS_Store /*.xcframework +build/ xcuserdata/ DerivedData/ diff --git a/src/build/LipoStep.zig b/src/build/LipoStep.zig new file mode 100644 index 000000000..cc6f187ce --- /dev/null +++ b/src/build/LipoStep.zig @@ -0,0 +1,75 @@ +//! A zig builder step that runs "lipo" on two binaries to create +//! a universal binary. +const LipoStep = @This(); + +const std = @import("std"); +const Step = std.build.Step; +const FileSource = std.build.FileSource; +const GeneratedFile = std.build.GeneratedFile; + +pub const Options = struct { + /// The name of the xcframework to create. + name: []const u8, + + /// The filename (not the path) of the file to create. + out_name: []const u8, + + /// Library file (dylib, a) to package. + input_a: FileSource, + input_b: FileSource, +}; + +step: Step, +builder: *std.build.Builder, + +/// Resulting binary +out_path: GeneratedFile, + +/// See Options +name: []const u8, +out_name: []const u8, +input_a: FileSource, +input_b: FileSource, + +pub fn create(builder: *std.build.Builder, opts: Options) *LipoStep { + const self = builder.allocator.create(LipoStep) catch @panic("OOM"); + self.* = .{ + .step = Step.init(.custom, builder.fmt("lipo {s}", .{opts.name}), builder.allocator, make), + .builder = builder, + .name = opts.name, + .out_path = .{ .step = &self.step }, + .out_name = opts.out_name, + .input_a = opts.input_a, + .input_b = opts.input_b, + }; + return self; +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(LipoStep, "step", step); + + // TODO: use the zig cache system when it is in the stdlib + // https://github.com/ziglang/zig/pull/14571 + const output_path = self.builder.pathJoin(&.{ + self.builder.cache_root, self.out_name, + }); + + // We use a RunStep here to ease our configuration. + { + const run = std.build.RunStep.create(self.builder, self.builder.fmt( + "lipo {s}", + .{self.name}, + )); + run.addArgs(&.{ + "lipo", + "-create", + "-output", + output_path, + self.input_a.getPath(self.builder), + self.input_b.getPath(self.builder), + }); + try run.step.make(); + } + + self.out_path.path = output_path; +}