diff --git a/src/build/CombineArchivesStep.zig b/src/build/CombineArchivesStep.zig new file mode 100644 index 000000000..98e6b037c --- /dev/null +++ b/src/build/CombineArchivesStep.zig @@ -0,0 +1,47 @@ +//! Combines multiple static archives into a single fat archive. +//! Uses libtool on Darwin and a cross-platform MRI-script build tool +//! on all other platforms (including Windows). +const std = @import("std"); +const LibtoolStep = @import("LibtoolStep.zig"); + +/// Combine multiple static archives into a single fat archive. +/// +/// `name` identifies the library (e.g. "ghostty-internal", "ghostty-vt"). +/// Output uses a `-fat` suffix to distinguish the combined archive from +/// the single-library archive in the build cache. +pub fn create( + b: *std.Build, + target: std.Build.ResolvedTarget, + name: []const u8, + sources: []const std.Build.LazyPath, +) struct { step: *std.Build.Step, output: std.Build.LazyPath } { + if (target.result.os.tag.isDarwin()) { + const libtool = LibtoolStep.create(b, .{ + .name = name, + .out_name = b.fmt("lib{s}-fat.a", .{name}), + .sources = @constCast(sources), + }); + return .{ .step = libtool.step, .output = libtool.output }; + } + + // On non-Darwin, use a build tool that generates an MRI script and + // pipes it to `zig ar -M`. This works on all platforms including + // Windows (the previous /bin/sh approach did not). + const tool = b.addExecutable(.{ + .name = "combine_archives", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/build/combine_archives.zig"), + .target = b.graph.host, + }), + }); + const run = b.addRunArtifact(tool); + run.addArg(b.graph.zig_exe); + const out_name = if (target.result.os.tag == .windows) + b.fmt("{s}-fat.lib", .{name}) + else + b.fmt("lib{s}-fat.a", .{name}); + const output = run.addOutputFileArg(out_name); + for (sources) |source| run.addFileArg(source); + + return .{ .step = &run.step, .output = output }; +} diff --git a/src/build/GhosttyLib.zig b/src/build/GhosttyLib.zig index 7e4968928..b762da8bb 100644 --- a/src/build/GhosttyLib.zig +++ b/src/build/GhosttyLib.zig @@ -2,9 +2,9 @@ const GhosttyLib = @This(); const std = @import("std"); const RunStep = std.Build.Step.Run; +const CombineArchivesStep = @import("CombineArchivesStep.zig"); const Config = @import("Config.zig"); const SharedDeps = @import("SharedDeps.zig"); -const LibtoolStep = @import("LibtoolStep.zig"); const LipoStep = @import("LipoStep.zig"); /// The step that generates the file. @@ -54,7 +54,7 @@ pub fn initStatic( // Combine all archives into a single fat static library so // consumers only need to link one file. - const combined = combineArchives(b, deps.config.target, lib_list.items); + const combined = CombineArchivesStep.create(b, deps.config.target, "ghostty-internal", lib_list.items); combined.step.dependOn(&lib.step); return .{ @@ -221,61 +221,6 @@ pub fn installHeader(self: *const GhosttyLib) void { b.getInstallStep().dependOn(&header_install.step); } -/// Combine multiple static archives into a single fat archive. -/// Uses libtool on Darwin, lib.exe on Windows, and ar MRI scripts -/// on other platforms. -fn combineArchives( - b: *std.Build, - target: std.Build.ResolvedTarget, - sources: []const std.Build.LazyPath, -) struct { step: *std.Build.Step, output: std.Build.LazyPath } { - const os_tag = target.result.os.tag; - - if (os_tag.isDarwin()) { - const libtool = LibtoolStep.create(b, .{ - .name = "ghostty", - .out_name = "libghostty-fat.a", - .sources = @constCast(sources), - }); - return .{ .step = libtool.step, .output = libtool.output }; - } - - if (os_tag == .windows) { - // Zig's bundled LLVM archiver can flatten COFF archives with - // the L modifier. MSVC's lib.exe cannot read Zig-produced - // GNU-format archives, so we use zig ar instead. - const run = RunStep.create(b, "combine-archives ghostty"); - run.addArgs(&.{ b.graph.zig_exe, "ar", "qcL", "--format=coff" }); - const output = run.addOutputFileArg("ghostty-fat.lib"); - for (sources) |source| run.addFileArg(source); - return .{ .step = &run.step, .output = output }; - } - - // On Linux and other platforms, use an MRI script with ar -M to - // combine archives directly without extracting. - const run = RunStep.create(b, "combine-archives ghostty"); - run.addArgs(&.{ - "/bin/sh", "-c", - \\set -e - \\out="$1"; shift - \\script="CREATE $out" - \\for a in "$@"; do - \\ script="$script - \\ADDLIB $a" - \\done - \\script="$script - \\SAVE - \\END" - \\echo "$script" | ar -M - , - "_", - }); - const output = run.addOutputFileArg("libghostty-fat.a"); - for (sources) |source| run.addFileArg(source); - - return .{ .step = &run.step, .output = output }; -} - const PkgConfigFiles = struct { shared: std.Build.LazyPath, static: std.Build.LazyPath, diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig index c59f40de7..65f945bfd 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -4,9 +4,9 @@ const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; const RunStep = std.Build.Step.Run; +const CombineArchivesStep = @import("CombineArchivesStep.zig"); const Config = @import("Config.zig"); const GhosttyZig = @import("GhosttyZig.zig"); -const LibtoolStep = @import("LibtoolStep.zig"); const LipoStep = @import("LipoStep.zig"); const SharedDeps = @import("SharedDeps.zig"); const XCFrameworkStep = @import("XCFrameworkStep.zig"); @@ -287,7 +287,7 @@ fn initLib( try sources.append(b.allocator, lib.getEmittedBin()); try sources.appendSlice(b.allocator, zig.simd_libs.items); - const combined = combineArchives(b, target, sources.items); + const combined = CombineArchivesStep.create(b, target, "ghostty-vt", sources.items); combined.step.dependOn(&lib.step); return .{ @@ -312,40 +312,6 @@ fn initLib( }; } -/// Combine multiple static archives into a single fat archive. -/// Uses libtool on Darwin and ar MRI scripts on other platforms. -fn combineArchives( - b: *std.Build, - target: std.Build.ResolvedTarget, - sources: []const std.Build.LazyPath, -) struct { step: *std.Build.Step, output: std.Build.LazyPath } { - if (target.result.os.tag.isDarwin()) { - const libtool = LibtoolStep.create(b, .{ - .name = "ghostty-vt", - .out_name = "libghostty-vt.a", - .sources = @constCast(sources), - }); - return .{ .step = libtool.step, .output = libtool.output }; - } - - // On non-Darwin, use a build tool that generates an MRI script and - // pipes it to `zig ar -M`. This works on all platforms including - // Windows (the previous /bin/sh approach did not). - const tool = b.addExecutable(.{ - .name = "combine_archives", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/build/combine_archives.zig"), - .target = b.graph.host, - }), - }); - const run = b.addRunArtifact(tool); - run.addArg(b.graph.zig_exe); - const output = run.addOutputFileArg("libghostty-vt.a"); - for (sources) |source| run.addFileArg(source); - - return .{ .step = &run.step, .output = output }; -} - /// Returns the Libs.private value for the pkg-config file. /// Vendored C++ dependencies are built in no-libcxx mode so consumers /// don't need libc++. System-provided simdutf still requires it.