mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-19 14:00:29 +00:00
build: add pkg-config static linking support and fat archives to libghostty
The libghostty-vt pkg-config file was missing Libs.private, so pkg-config --libs --static returned the same flags as the shared case, omitting the C++ standard library needed by the SIMD code. Additionally, the static archive did not bundle the vendored SIMD dependencies (simdutf, highway, utfcpp), leaving consumers with unresolved symbols when linking. If we're choosing to vendor (no -fsys) then we should produce a fat static archive that includes them. If `-fsys` is used, then we should not bundle them and instead reference them via Requires.private, letting pkg-config chain to their own .pc files. Add Libs.private with the C++ runtime (-lc++ on Darwin, -lstdc++ on Linux) and Requires.private for any SIMD deps provided via system integration. When SIMD deps are vendored (the default), produce a fat static archive that bundles them using libtool on Darwin and ar on Linux. When they come from the system (-fsys=), reference them via Requires.private instead, letting pkg-config chain to their own .pc files.
This commit is contained in:
@@ -5,6 +5,8 @@ const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const RunStep = std.Build.Step.Run;
|
||||
const GhosttyZig = @import("GhosttyZig.zig");
|
||||
const LibtoolStep = @import("LibtoolStep.zig");
|
||||
const SharedDeps = @import("SharedDeps.zig");
|
||||
|
||||
/// The step that generates the file.
|
||||
step: *std.Build.Step,
|
||||
@@ -185,9 +187,35 @@ fn initLib(
|
||||
\\Version: 0.1.0
|
||||
\\Cflags: -I${{includedir}}
|
||||
\\Libs: -L${{libdir}} -lghostty-vt
|
||||
, .{b.install_prefix}));
|
||||
\\Libs.private: {s}
|
||||
\\Requires.private: {s}
|
||||
, .{ b.install_prefix, libsPrivate(zig), requiresPrivate(b) }));
|
||||
};
|
||||
|
||||
// For static libraries with vendored SIMD dependencies, combine
|
||||
// all archives into a single fat archive so consumers only need
|
||||
// to link one file. Skip on Windows where ar/libtool aren't available.
|
||||
if (kind == .static and
|
||||
zig.simd_libs.items.len > 0 and
|
||||
target.result.os.tag != .windows)
|
||||
{
|
||||
var sources: SharedDeps.LazyPathList = .empty;
|
||||
try sources.append(b.allocator, lib.getEmittedBin());
|
||||
try sources.appendSlice(b.allocator, zig.simd_libs.items);
|
||||
|
||||
const combined = combineArchives(b, target, sources.items);
|
||||
combined.step.dependOn(&lib.step);
|
||||
|
||||
return .{
|
||||
.step = combined.step,
|
||||
.artifact = &b.addInstallArtifact(lib, .{}).step,
|
||||
.kind = kind,
|
||||
.output = combined.output,
|
||||
.dsym = dsymutil,
|
||||
.pkg_config = pc,
|
||||
};
|
||||
}
|
||||
|
||||
return .{
|
||||
.step = &lib.step,
|
||||
.artifact = &b.addInstallArtifact(lib, .{}).step,
|
||||
@@ -198,6 +226,74 @@ 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 an MRI script with ar -M to combine archives
|
||||
// directly without extracting. This avoids issues with ar x
|
||||
// producing full-path member names and read-only permissions.
|
||||
const run = RunStep.create(b, "combine-archives ghostty-vt");
|
||||
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-vt.a");
|
||||
for (sources) |source| run.addFileArg(source);
|
||||
|
||||
return .{ .step = &run.step, .output = output };
|
||||
}
|
||||
|
||||
/// Returns the Libs.private value for the pkg-config file.
|
||||
/// This includes the C++ standard library needed by SIMD code.
|
||||
///
|
||||
/// Zig compiles C++ code with LLVM's libc++ (not GNU libstdc++),
|
||||
/// so consumers linking the static library need a libc++-compatible
|
||||
/// toolchain: `zig cc`, `clang`, or GCC with `-lc++` installed.
|
||||
fn libsPrivate(
|
||||
zig: *const GhosttyZig,
|
||||
) []const u8 {
|
||||
return if (zig.vt_c.link_libcpp orelse false) "-lc++" else "";
|
||||
}
|
||||
|
||||
/// Returns the Requires.private value for the pkg-config file.
|
||||
/// When SIMD dependencies are provided by the system (via
|
||||
/// -Dsystem-integration), we reference their pkg-config names so
|
||||
/// that downstream consumers pick them up transitively.
|
||||
fn requiresPrivate(b: *std.Build) []const u8 {
|
||||
const system_simdutf = b.systemIntegrationOption("simdutf", .{});
|
||||
const system_highway = b.systemIntegrationOption("highway", .{ .default = false });
|
||||
|
||||
if (system_simdutf and system_highway) return "simdutf, libhwy";
|
||||
if (system_simdutf) return "simdutf";
|
||||
if (system_highway) return "libhwy";
|
||||
return "";
|
||||
}
|
||||
|
||||
pub fn install(
|
||||
self: *const GhosttyLibVt,
|
||||
step: *std.Build.Step,
|
||||
|
||||
@@ -11,6 +11,12 @@ const TerminalBuildOptions = @import("../terminal/build_options.zig").Options;
|
||||
vt: *std.Build.Module,
|
||||
vt_c: *std.Build.Module,
|
||||
|
||||
/// Static library paths for vendored SIMD dependencies. Populated
|
||||
/// only when the dependencies are built from source (not provided
|
||||
/// by the system via -Dsystem-integration). Used to produce a
|
||||
/// combined static archive for downstream consumers.
|
||||
simd_libs: SharedDeps.LazyPathList,
|
||||
|
||||
pub fn init(
|
||||
b: *std.Build,
|
||||
cfg: *const Config,
|
||||
@@ -24,6 +30,8 @@ pub fn init(
|
||||
// conditionally do this.
|
||||
vt_options.oniguruma = false;
|
||||
|
||||
var simd_libs: SharedDeps.LazyPathList = .empty;
|
||||
|
||||
return .{
|
||||
.vt = try initVt(
|
||||
"ghostty-vt",
|
||||
@@ -31,6 +39,7 @@ pub fn init(
|
||||
cfg,
|
||||
deps,
|
||||
vt_options,
|
||||
null,
|
||||
),
|
||||
|
||||
.vt_c = try initVt(
|
||||
@@ -43,7 +52,10 @@ pub fn init(
|
||||
dup.c_abi = true;
|
||||
break :options dup;
|
||||
},
|
||||
&simd_libs,
|
||||
),
|
||||
|
||||
.simd_libs = simd_libs,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,6 +65,7 @@ fn initVt(
|
||||
cfg: *const Config,
|
||||
deps: *const SharedDeps,
|
||||
vt_options: TerminalBuildOptions,
|
||||
simd_libs: ?*SharedDeps.LazyPathList,
|
||||
) !*std.Build.Module {
|
||||
// General build options
|
||||
const general_options = b.addOptions();
|
||||
@@ -82,7 +95,7 @@ fn initVt(
|
||||
|
||||
// If SIMD is enabled, add all our SIMD dependencies.
|
||||
if (cfg.simd) {
|
||||
try SharedDeps.addSimd(b, vt, null);
|
||||
try SharedDeps.addSimd(b, vt, simd_libs);
|
||||
}
|
||||
|
||||
return vt;
|
||||
|
||||
Reference in New Issue
Block a user