build: add libghostty-vt-static pkg-config module

Keep libghostty-vt.pc as the shared/default pkg-config module so
`pkg-config --static libghostty-vt` continues to emit the historical
`-lghostty-vt` flags. This preserves the old behavior for consumers
that still want it, even though that form remains ambiguous on macOS
when both the dylib and archive are installed in the same directory.

Add a separate libghostty-vt-static.pc module for consumers that need
an unambiguous static link. Its `Libs:` entry points directly at the
installed archive so macOS does not resolve the request to the dylib.

Update the Nix packaging to rewrite the new static module into the `dev`
output, use it in the static-link smoke test, and add a compatibility
check that covers both pkg-config entry points.
This commit is contained in:
Mitchell Hashimoto
2026-04-09 20:18:54 -07:00
parent 1a5bfbd87c
commit a82e156925
2 changed files with 100 additions and 24 deletions

View File

@@ -80,6 +80,8 @@ stdenv.mkDerivation (finalAttrs: {
postFixup = ''
substituteInPlace "$dev/share/pkgconfig/libghostty-vt.pc" \
--replace-fail "$out" "$dev"
substituteInPlace "$dev/share/pkgconfig/libghostty-vt-static.pc" \
--replace-fail "$out" "$dev"
'';
passthru.tests = {
@@ -105,6 +107,17 @@ stdenv.mkDerivation (finalAttrs: {
pkg-config = testers.hasPkgConfigModules {
package = finalAttrs.finalPackage.dev;
};
pkg-config-libs =
runCommand "pkg-config-libs" {
nativeBuildInputs = [pkg-config];
} ''
export PKG_CONFIG_PATH="${finalAttrs.finalPackage.dev}/share/pkgconfig"
pkg-config --libs --static libghostty-vt | grep -q -- '-lghostty-vt'
pkg-config --libs --static libghostty-vt-static | grep -q -- '${finalAttrs.finalPackage.dev}/lib/libghostty-vt.a'
touch "$out"
'';
build-with-shared = stdenv.mkDerivation {
name = "build-with-shared";
src = ./test-src;
@@ -154,10 +167,7 @@ stdenv.mkDerivation (finalAttrs: {
runHook preBuildHooks
cc -o test test_libghostty_vt.c \
''$(pkg-config --cflags libghostty-vt) \
${finalAttrs.finalPackage.dev}/lib/libghostty-vt.a \
''$(pkg-config --libs-only-l --static libghostty-vt | sed 's/-lghostty-vt//') \
-Wl,-rpath,"${finalAttrs.finalPackage}/lib"
''$(pkg-config --cflags --libs --static libghostty-vt-static)
runHook postBuildHooks
'';
@@ -227,6 +237,9 @@ stdenv.mkDerivation (finalAttrs: {
homepage = "https://ghostty.org";
license = lib.licenses.mit;
platforms = zig_0_15.meta.platforms;
pkgConfigModules = ["libghostty-vt"];
pkgConfigModules = [
"libghostty-vt"
"libghostty-vt-static"
];
};
})

View File

@@ -24,6 +24,7 @@ kind: Kind,
output: std.Build.LazyPath,
dsym: ?std.Build.LazyPath,
pkg_config: ?std.Build.LazyPath,
pkg_config_static: ?std.Build.LazyPath,
/// The kind of library being built. This is similar to LinkMode but
/// also includes wasm which is an executable, not a library.
@@ -85,6 +86,7 @@ pub fn initWasm(
.output = output,
.dsym = null,
.pkg_config = null,
.pkg_config_static = null,
};
}
@@ -162,6 +164,7 @@ pub fn initStaticAppleUniversal(
.output = universal.output,
.dsym = null,
.pkg_config = null,
.pkg_config_static = null,
});
// Additional Apple platforms, each gated on SDK availability.
@@ -264,23 +267,15 @@ fn initLib(
};
// pkg-config
const pc: std.Build.LazyPath = pc: {
const wf = b.addWriteFiles();
break :pc wf.add("libghostty-vt.pc", b.fmt(
\\prefix={s}
\\includedir=${{prefix}}/include
\\libdir=${{prefix}}/lib
\\
\\Name: libghostty-vt
\\URL: https://github.com/ghostty-org/ghostty
\\Description: Ghostty VT library
\\Version: {f}
\\Cflags: -I${{includedir}}
\\Libs: -L${{libdir}} -lghostty-vt
\\Libs.private: {s}
\\Requires.private: {s}
, .{ b.install_prefix, zig.version, libsPrivate(zig), requiresPrivate(b) }));
};
//
// pkg-config's --static only expands Libs.private / Requires.private;
// it doesn't change -lghostty-vt into an archive-only reference when
// both shared and static libraries are installed. Install a dedicated
// static module so consumers can request the archive explicitly.
const pcs: ?PkgConfigFiles = if (kind == .shared)
pkgConfigFiles(b, zig, target.result.os.tag)
else
null;
// For static libraries with vendored SIMD dependencies, combine
// all archives into a single fat archive so consumers only need
@@ -302,7 +297,8 @@ fn initLib(
.kind = kind,
.output = combined.output,
.dsym = dsymutil,
.pkg_config = pc,
.pkg_config = if (pcs) |v| v.shared else null,
.pkg_config_static = if (pcs) |v| v.static else null,
};
}
@@ -312,7 +308,8 @@ fn initLib(
.kind = kind,
.output = lib.getEmittedBin(),
.dsym = dsymutil,
.pkg_config = pc,
.pkg_config = if (pcs) |v| v.shared else null,
.pkg_config_static = if (pcs) |v| v.static else null,
};
}
@@ -370,6 +367,65 @@ fn libsPrivate(
return if (zig.vt_c.link_libcpp orelse false) "-lc++" else "";
}
const PkgConfigFiles = struct {
shared: std.Build.LazyPath,
static: std.Build.LazyPath,
};
fn pkgConfigFiles(
b: *std.Build,
zig: *const GhosttyZig,
os_tag: std.Target.Os.Tag,
) PkgConfigFiles {
const wf = b.addWriteFiles();
const libs_private = libsPrivate(zig);
const requires_private = requiresPrivate(b);
return .{
.shared = wf.add("libghostty-vt.pc", b.fmt(
\\prefix={s}
\\includedir=${{prefix}}/include
\\libdir=${{prefix}}/lib
\\
\\Name: libghostty-vt
\\URL: https://github.com/ghostty-org/ghostty
\\Description: Ghostty VT library
\\Version: {f}
\\Cflags: -I${{includedir}}
\\Libs: -L${{libdir}} -lghostty-vt
\\Libs.private: {s}
\\Requires.private: {s}
, .{ b.install_prefix, zig.version, libs_private, requires_private })),
.static = wf.add("libghostty-vt-static.pc", b.fmt(
\\prefix={s}
\\includedir=${{prefix}}/include
\\libdir=${{prefix}}/lib
\\
\\Name: libghostty-vt-static
\\URL: https://github.com/ghostty-org/ghostty
\\Description: Ghostty VT library (static)
\\Version: {f}
\\Cflags: -I${{includedir}}
\\Libs: ${{libdir}}/{s}
\\Libs.private: {s}
\\Requires.private: {s}
, .{
b.install_prefix,
zig.version,
staticLibraryName(os_tag),
libs_private,
requires_private,
})),
};
}
fn staticLibraryName(os_tag: std.Target.Os.Tag) []const u8 {
return if (os_tag == .windows)
"ghostty-vt-static.lib"
else
"libghostty-vt.a";
}
/// 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
@@ -450,4 +506,11 @@ pub fn install(
"share/pkgconfig/libghostty-vt.pc",
).step);
}
if (self.pkg_config_static) |pkg_config_static| {
step.dependOn(&b.addInstallFileWithDir(
pkg_config_static,
.prefix,
"share/pkgconfig/libghostty-vt-static.pc",
).step);
}
}