From b006101ddd140002f87e09ae727ea23064ddc0d7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 23 Sep 2025 12:33:22 -0700 Subject: [PATCH] lib-vt: boilerplate to build a shared object --- build.zig | 12 ++++++- include/ghostty-vt.h | 14 ++++++++ src/build/GhosttyLibVt.zig | 72 ++++++++++++++++++++++++++++++++++++++ src/build/main.zig | 1 + src/lib_vt.zig | 6 ++++ src/terminal/c_api.zig | 5 +++ src/terminal/main.zig | 7 ++++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 include/ghostty-vt.h create mode 100644 src/build/GhosttyLibVt.zig create mode 100644 src/terminal/c_api.zig diff --git a/build.zig b/build.zig index 8838572b7..5ec69d40e 100644 --- a/build.zig +++ b/build.zig @@ -31,6 +31,7 @@ pub fn build(b: *std.Build) !void { // All our steps which we'll hook up later. The steps are shown // up here just so that they are more self-documenting. + const libvt_step = b.step("lib-vt", "Build libghostty-vt"); const run_step = b.step("run", "Run the app"); const run_valgrind_step = b.step( "run-valgrind", @@ -86,7 +87,7 @@ pub fn build(b: *std.Build) !void { check_step.dependOn(dist.install_step); } - // libghostty + // libghostty (internal, big) const libghostty_shared = try buildpkg.GhosttyLib.initShared( b, &deps, @@ -96,6 +97,15 @@ pub fn build(b: *std.Build) !void { &deps, ); + // libghostty-vt + const libghostty_vt_shared = try buildpkg.GhosttyLibVt.initShared( + b, + &mod, + &deps, + ); + libghostty_vt_shared.install(libvt_step, "libghostty-vt.so"); + libghostty_vt_shared.installHeader(libvt_step); + // Helpgen if (config.emit_helpgen) deps.help_strings.install(); diff --git a/include/ghostty-vt.h b/include/ghostty-vt.h new file mode 100644 index 000000000..591b095a2 --- /dev/null +++ b/include/ghostty-vt.h @@ -0,0 +1,14 @@ +// libghostty-vt + +#ifndef GHOSTTY_VT_H +#define GHOSTTY_VT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* GHOSTTY_VT_H */ diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig new file mode 100644 index 000000000..9f0646624 --- /dev/null +++ b/src/build/GhosttyLibVt.zig @@ -0,0 +1,72 @@ +const GhosttyLibVt = @This(); + +const std = @import("std"); +const RunStep = std.Build.Step.Run; +const Config = @import("Config.zig"); +const GhosttyZig = @import("GhosttyZig.zig"); +const SharedDeps = @import("SharedDeps.zig"); +const LibtoolStep = @import("LibtoolStep.zig"); +const LipoStep = @import("LipoStep.zig"); + +/// The step that generates the file. +step: *std.Build.Step, + +/// The final library file +output: std.Build.LazyPath, +dsym: ?std.Build.LazyPath, + +pub fn initShared( + b: *std.Build, + zig: *const GhosttyZig, + deps: *const SharedDeps, +) !GhosttyLibVt { + const lib = b.addSharedLibrary(.{ + .name = "ghostty-vt", + .root_module = zig.vt, + }); + + // Get our debug symbols + const dsymutil: ?std.Build.LazyPath = dsymutil: { + if (!deps.config.target.result.os.tag.isDarwin()) { + break :dsymutil null; + } + + const dsymutil = RunStep.create(b, "dsymutil"); + dsymutil.addArgs(&.{"dsymutil"}); + dsymutil.addFileArg(lib.getEmittedBin()); + dsymutil.addArgs(&.{"-o"}); + const output = dsymutil.addOutputFileArg("libghostty-vt.dSYM"); + break :dsymutil output; + }; + + return .{ + .step = &lib.step, + .output = lib.getEmittedBin(), + .dsym = dsymutil, + }; +} + +pub fn install( + self: *const GhosttyLibVt, + step: *std.Build.Step, + name: []const u8, +) void { + const b = self.step.owner; + const lib_install = b.addInstallLibFile( + self.output, + name, + ); + step.dependOn(&lib_install.step); +} + +pub fn installHeader( + self: *const GhosttyLibVt, + step: *std.Build.Step, +) void { + const b = self.step.owner; + const header_install = b.addInstallHeaderFile( + b.path("include/ghostty-vt.h"), + "ghostty-vt.h", + ); + step.dependOn(&header_install.step); +} diff --git a/src/build/main.zig b/src/build/main.zig index 0ee41352b..1f36d375c 100644 --- a/src/build/main.zig +++ b/src/build/main.zig @@ -13,6 +13,7 @@ pub const GhosttyDocs = @import("GhosttyDocs.zig"); pub const GhosttyExe = @import("GhosttyExe.zig"); pub const GhosttyFrameData = @import("GhosttyFrameData.zig"); pub const GhosttyLib = @import("GhosttyLib.zig"); +pub const GhosttyLibVt = @import("GhosttyLibVt.zig"); pub const GhosttyResources = @import("GhosttyResources.zig"); pub const GhosttyI18n = @import("GhosttyI18n.zig"); pub const GhosttyXcodebuild = @import("GhosttyXcodebuild.zig"); diff --git a/src/lib_vt.zig b/src/lib_vt.zig index d375a89d2..444f08d3c 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -65,6 +65,12 @@ pub const EraseLine = terminal.EraseLine; pub const TabClear = terminal.TabClear; pub const Attribute = terminal.Attribute; +comptime { + if (terminal.is_c_lib) { + _ = terminal.c_api; + } +} + test { _ = terminal; } diff --git a/src/terminal/c_api.zig b/src/terminal/c_api.zig new file mode 100644 index 000000000..287711fec --- /dev/null +++ b/src/terminal/c_api.zig @@ -0,0 +1,5 @@ +pub export fn ghostty_hi() void { + // Does nothing, but you can see this symbol exists: + // nm -D --defined-only zig-out/lib/libghostty-vt.so | rg ' T ' + // This is temporary as we figure out the API. +} diff --git a/src/terminal/main.zig b/src/terminal/main.zig index 1fea9934e..4106786e1 100644 --- a/src/terminal/main.zig +++ b/src/terminal/main.zig @@ -62,6 +62,13 @@ pub const Attribute = sgr.Attribute; pub const isSafePaste = sanitize.isSafePaste; +/// This is set to true when we're building the C library. +pub const is_c_lib = @import("root") == @import("../lib_vt.zig"); + +/// This is the C API for this package. Do NOT reference this unless +/// you want a bunch of symbols exported into your final artifact. +pub const c_api = @import("c_api.zig"); + test { @import("std").testing.refAllDecls(@This());