diff --git a/build.zig b/build.zig index 008fc849e..62fa77511 100644 --- a/build.zig +++ b/build.zig @@ -255,6 +255,15 @@ pub fn build(b: *std.Build) !void { }); const mod_vt_test_run = b.addRunArtifact(mod_vt_test); test_lib_vt_step.dependOn(&mod_vt_test_run.step); + + const mod_vt_c_test = b.addTest(.{ + .root_module = mod.vt_c, + .target = config.target, + .optimize = config.optimize, + .filters = test_filters, + }); + const mod_vt_c_test_run = b.addRunArtifact(mod_vt_c_test); + test_lib_vt_step.dependOn(&mod_vt_c_test_run.step); } // Tests diff --git a/src/build/Config.zig b/src/build/Config.zig index 474674d3a..0b7dae14d 100644 --- a/src/build/Config.zig +++ b/src/build/Config.zig @@ -498,6 +498,7 @@ pub fn terminalOptions(self: *const Config) TerminalBuildOptions { .artifact = .ghostty, .simd = self.simd, .oniguruma = true, + .c_abi = false, .slow_runtime_safety = switch (self.optimize) { .Debug => true, .ReleaseSafe, diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig index 0029d6756..80f2bf9ad 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -26,7 +26,7 @@ pub fn initShared( const target = zig.vt.resolved_target.?; const lib = b.addSharedLibrary(.{ .name = "ghostty-vt", - .root_module = zig.vt, + .root_module = zig.vt_c, }); lib.installHeader( b.path("include/ghostty/vt.h"), diff --git a/src/build/GhosttyZig.zig b/src/build/GhosttyZig.zig index f175eb957..a8d2726bc 100644 --- a/src/build/GhosttyZig.zig +++ b/src/build/GhosttyZig.zig @@ -5,18 +5,17 @@ const GhosttyZig = @This(); const std = @import("std"); const Config = @import("Config.zig"); const SharedDeps = @import("SharedDeps.zig"); +const TerminalBuildOptions = @import("../terminal/build_options.zig").Options; +/// The `_c`-suffixed modules are built with the C ABI enabled. vt: *std.Build.Module, +vt_c: *std.Build.Module, pub fn init( b: *std.Build, cfg: *const Config, deps: *const SharedDeps, ) !GhosttyZig { - // General build options - const general_options = b.addOptions(); - try cfg.addOptions(general_options); - // Terminal module build options var vt_options = cfg.terminalOptions(); vt_options.artifact = .lib; @@ -25,7 +24,41 @@ pub fn init( // conditionally do this. vt_options.oniguruma = false; - const vt = b.addModule("ghostty-vt", .{ + return .{ + .vt = try initVt( + "ghostty-vt", + b, + cfg, + deps, + vt_options, + ), + + .vt_c = try initVt( + "ghostty-vt-c", + b, + cfg, + deps, + options: { + var dup = vt_options; + dup.c_abi = true; + break :options dup; + }, + ), + }; +} + +fn initVt( + name: []const u8, + b: *std.Build, + cfg: *const Config, + deps: *const SharedDeps, + vt_options: TerminalBuildOptions, +) !*std.Build.Module { + // General build options + const general_options = b.addOptions(); + try cfg.addOptions(general_options); + + const vt = b.addModule(name, .{ .root_source_file = b.path("src/lib_vt.zig"), .target = cfg.target, .optimize = cfg.optimize, @@ -45,5 +78,5 @@ pub fn init( try SharedDeps.addSimd(b, vt, null); } - return .{ .vt = vt }; + return vt; } diff --git a/src/lib_vt.zig b/src/lib_vt.zig index 37ab7ae68..b7ef9459a 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -7,6 +7,7 @@ //! by thousands of users for years. However, the API itself (functions, //! types, etc.) may change without warning. We're working on stabilizing //! this in the future. +const lib = @This(); // The public API below reproduces a lot of terminal/main.zig but // is separate because (1) we need our root file to be in `src/` @@ -68,7 +69,7 @@ pub const Attribute = terminal.Attribute; comptime { // If we're building the C library (vs. the Zig module) then // we want to reference the C API so that it gets exported. - if (terminal.is_c_lib) { + if (@import("root") == lib) { const c = terminal.c_api; @export(&c.osc_new, .{ .name = "ghostty_osc_new" }); @export(&c.osc_free, .{ .name = "ghostty_osc_free" }); @@ -81,8 +82,8 @@ comptime { test { _ = terminal; - - // Tests always test the C API and shared C functions - _ = terminal.c_api; _ = @import("lib/main.zig"); + if (comptime terminal.options.c_abi) { + _ = terminal.c_api; + } } diff --git a/src/terminal/build_options.zig b/src/terminal/build_options.zig index 2085e2243..e209a56fa 100644 --- a/src/terminal/build_options.zig +++ b/src/terminal/build_options.zig @@ -1,8 +1,6 @@ const std = @import("std"); -/// True if we're building the C library libghostty-vt. -pub const is_c_lib = @import("root") == @import("../lib_vt.zig"); - +/// Options set by Zig build.zig and exposed via `terminal_options`. pub const Options = struct { /// The target artifact to build. This will gate some functionality. artifact: Artifact, @@ -26,6 +24,10 @@ pub const Options = struct { /// generally be disabled in production builds. slow_runtime_safety: bool, + /// Force C ABI mode on or off. If not set, then it will be set based on + /// Options. + c_abi: bool, + /// Add the required build options for the terminal module. pub fn add( self: Options, @@ -34,6 +36,7 @@ pub const Options = struct { ) void { const opts = b.addOptions(); opts.addOption(Artifact, "artifact", self.artifact); + opts.addOption(bool, "c_abi", self.c_abi); opts.addOption(bool, "oniguruma", self.oniguruma); opts.addOption(bool, "simd", self.simd); opts.addOption(bool, "slow_runtime_safety", self.slow_runtime_safety); diff --git a/src/terminal/c/osc.zig b/src/terminal/c/osc.zig index c04626b69..d1998f4e1 100644 --- a/src/terminal/c/osc.zig +++ b/src/terminal/c/osc.zig @@ -73,9 +73,9 @@ test "command type" { )); defer free(p); - p.next('0'); - p.next(';'); - p.next('a'); - const cmd = p.end(0); + next(p, '0'); + next(p, ';'); + next(p, 'a'); + const cmd = end(p, 0); try testing.expectEqual(.change_window_title, commandType(cmd)); } diff --git a/src/terminal/main.zig b/src/terminal/main.zig index 7403ff309..6875ba89d 100644 --- a/src/terminal/main.zig +++ b/src/terminal/main.zig @@ -1,5 +1,4 @@ const builtin = @import("builtin"); -const build_options = @import("terminal_options"); const charsets = @import("charsets.zig"); const sanitize = @import("sanitize.zig"); @@ -21,7 +20,7 @@ pub const page = @import("page.zig"); pub const parse_table = @import("parse_table.zig"); pub const search = @import("search.zig"); pub const size = @import("size.zig"); -pub const tmux = if (build_options.tmux_control_mode) @import("tmux.zig") else struct {}; +pub const tmux = if (options.tmux_control_mode) @import("tmux.zig") else struct {}; pub const x11_color = @import("x11_color.zig"); pub const Charset = charsets.Charset; @@ -62,9 +61,11 @@ pub const Attribute = sgr.Attribute; pub const isSafePaste = sanitize.isSafePaste; +pub const Options = @import("build_options.zig").Options; +pub const options = @import("terminal_options"); + /// This is set to true when we're building the C library. -pub const is_c_lib = @import("build_options.zig").is_c_lib; -pub const c_api = if (is_c_lib) @import("c/main.zig") else void; +pub const c_api = if (options.c_abi) @import("c/main.zig") else void; test { @import("std").testing.refAllDecls(@This()); diff --git a/src/terminal/osc.zig b/src/terminal/osc.zig index 71d2f8598..20b22d1ef 100644 --- a/src/terminal/osc.zig +++ b/src/terminal/osc.zig @@ -7,11 +7,11 @@ const osc = @This(); const std = @import("std"); const builtin = @import("builtin"); +const build_options = @import("terminal_options"); const mem = std.mem; const assert = std.debug.assert; const Allocator = mem.Allocator; const LibEnum = @import("../lib/enum.zig").Enum; -const is_c_lib = @import("build_options.zig").is_c_lib; const RGB = @import("color.zig").RGB; const kitty_color = @import("kitty/color.zig"); const osc_color = @import("osc/color.zig"); @@ -175,7 +175,7 @@ pub const Command = union(Key) { conemu_guimacro: []const u8, pub const Key = LibEnum( - if (is_c_lib) .c else .zig, + if (build_options.c_abi) .c else .zig, // NOTE: Order matters, see LibEnum documentation. &.{ "invalid",