diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-07 18:29:21 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-07 18:29:21 +0300 |
| commit | dcfe11a7588fcd42471159453810abc9b7720a57 (patch) | |
| tree | c32a77266028e73ca2382b96c96f8438a5fcd621 /vendor/libghostty-vt/src/build | |
| parent | 2dfed860103c4e4505ac7f25def9fe37da1e1e6a (diff) | |
build: update libghostty-vt vendor
vendor ghostty 063ac3e and remove the musl libc++ link workaround.
expose the new selection formatter and hyperlink uri apis for follow-up rendering work.
add manual artifact validation for static linux builds.
Diffstat (limited to 'vendor/libghostty-vt/src/build')
| -rw-r--r-- | vendor/libghostty-vt/src/build/CombineArchivesStep.zig | 50 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/Config.zig | 60 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/GhosttyLib.zig | 119 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/GhosttyLibVt.zig | 306 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/GhosttyXCFramework.zig | 18 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/GhosttyZig.zig | 62 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/GitVersion.zig | 13 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/SharedDeps.zig | 54 | ||||
| -rw-r--r-- | vendor/libghostty-vt/src/build/combine_archives.zig | 55 |
9 files changed, 595 insertions, 142 deletions
diff --git a/vendor/libghostty-vt/src/build/CombineArchivesStep.zig b/vendor/libghostty-vt/src/build/CombineArchivesStep.zig new file mode 100644 index 00000000..cebd8e9f --- /dev/null +++ b/vendor/libghostty-vt/src/build/CombineArchivesStep.zig @@ -0,0 +1,50 @@ +//! 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 builtin = @import("builtin"); +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() and + comptime builtin.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/vendor/libghostty-vt/src/build/Config.zig b/vendor/libghostty-vt/src/build/Config.zig index 88968aab..0a994731 100644 --- a/vendor/libghostty-vt/src/build/Config.zig +++ b/vendor/libghostty-vt/src/build/Config.zig @@ -38,6 +38,7 @@ wasm_shared: bool = true, /// Ghostty exe properties exe_entrypoint: ExeEntrypoint = .ghostty, version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }, +lib_version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }, /// Binary properties pie: bool = false, @@ -68,7 +69,7 @@ is_dep: bool = false, /// Environmental properties env: std.process.EnvMap, -pub fn init(b: *std.Build, appVersion: []const u8) !Config { +pub fn init(b: *std.Build, appVersion: []const u8, libVersion: []const u8) !Config { // Setup our standard Zig target and optimize options, i.e. // `-Doptimize` and `-Dtarget`. const optimize = b.standardOptimizeOption(.{}); @@ -294,6 +295,20 @@ pub fn init(b: *std.Build, appVersion: []const u8) !Config { }; }; + // libghostty-vt properties + + const lib_version_string = b.option( + []const u8, + "lib-version-string", + "A specific version string to use for the build of libghostty-vt. " ++ + "If not specified, git will be used. This must be a semantic version.", + ); + + config.lib_version = if (lib_version_string) |v| + try std.SemanticVersion.parse(v) + else + try std.SemanticVersion.parse(libVersion); + //--------------------------------------------------------------- // Binary Properties @@ -429,13 +444,22 @@ pub fn init(b: *std.Build, appVersion: []const u8) !Config { bool, "emit-xcframework", "Build and install the xcframework for the macOS library.", - ) orelse !config.emit_lib_vt and - builtin.target.os.tag.isDarwin() and - target.result.os.tag == .macos and - config.app_runtime == .none and - (!config.emit_bench and - !config.emit_test_exe and - !config.emit_helpgen); + ) orelse emit_xcfw: { + if (!builtin.target.os.tag.isDarwin() or target.result.os.tag != .macos) + break :emit_xcfw false; + if (config.emit_lib_vt) { + // In lib-vt mode default to whether xcodebuild is available, + // since xcodebuild is required to produce the XCFramework. + const path = expandPath(b.allocator, "xcodebuild") catch + break :emit_xcfw false; + defer if (path) |p| b.allocator.free(p); + break :emit_xcfw path != null; + } + break :emit_xcfw config.app_runtime == .none and + (!config.emit_bench and + !config.emit_test_exe and + !config.emit_helpgen); + }; config.emit_macos_app = b.option( bool, @@ -519,13 +543,20 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void { // Our version. We also add the string version so we don't need // to do any allocations at runtime. This has to be long enough to // accommodate realistic large branch names for dev versions. - var buf: [1024]u8 = undefined; + var app_version_buf: [1024]u8 = undefined; step.addOption(std.SemanticVersion, "app_version", self.version); step.addOption([:0]const u8, "app_version_string", try std.fmt.bufPrintZ( - &buf, + &app_version_buf, "{f}", .{self.version}, )); + var lib_version_buf: [1024]u8 = undefined; + step.addOption(std.SemanticVersion, "lib_version", self.lib_version); + step.addOption([:0]const u8, "lib_version_string", try std.fmt.bufPrintZ( + &lib_version_buf, + "{f}", + .{self.lib_version}, + )); step.addOption( ReleaseChannel, "release_channel", @@ -539,13 +570,16 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void { /// Returns the build options for the terminal module. This assumes a /// Ghostty executable being built. Callers should modify this as needed. -pub fn terminalOptions(self: *const Config) TerminalBuildOptions { +pub fn terminalOptions(self: *const Config, artifact: TerminalBuildOptions.Artifact) TerminalBuildOptions { return .{ - .artifact = .ghostty, + .artifact = artifact, .simd = self.simd, .oniguruma = true, .c_abi = false, - .version = self.version, + .version = switch (artifact) { + .ghostty => self.version, + .lib => self.lib_version, + }, .slow_runtime_safety = switch (self.optimize) { .Debug => true, .ReleaseSafe, diff --git a/vendor/libghostty-vt/src/build/GhosttyLib.zig b/vendor/libghostty-vt/src/build/GhosttyLib.zig index 4e15fbbf..b762da8b 100644 --- a/vendor/libghostty-vt/src/build/GhosttyLib.zig +++ b/vendor/libghostty-vt/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. @@ -13,6 +13,8 @@ step: *std.Build.Step, /// The final static library file output: std.Build.LazyPath, dsym: ?std.Build.LazyPath, +pkg_config: ?std.Build.LazyPath, +pkg_config_static: ?std.Build.LazyPath, pub fn initStatic( b: *std.Build, @@ -46,30 +48,23 @@ pub fn initStatic( } // Add our dependencies. Get the list of all static deps so we can - // build a combined archive if necessary. + // build a combined archive. var lib_list = try deps.add(lib); try lib_list.append(b.allocator, lib.getEmittedBin()); - if (!deps.config.target.result.os.tag.isDarwin()) return .{ - .step = &lib.step, - .output = lib.getEmittedBin(), - .dsym = null, - }; - - // Create a static lib that contains all our dependencies. - const libtool = LibtoolStep.create(b, .{ - .name = "ghostty", - .out_name = "libghostty-fat.a", - .sources = lib_list.items, - }); - libtool.step.dependOn(&lib.step); + // Combine all archives into a single fat static library so + // consumers only need to link one file. + const combined = CombineArchivesStep.create(b, deps.config.target, "ghostty-internal", lib_list.items); + combined.step.dependOn(&lib.step); return .{ - .step = libtool.step, - .output = libtool.output, + .step = combined.step, + .output = combined.output, // Static libraries cannot have dSYMs because they aren't linked. .dsym = null, + .pkg_config = null, + .pkg_config_static = null, }; } @@ -146,10 +141,20 @@ pub fn initShared( break :dsymutil output; }; + // pkg-config + // + // pkg-config's --static only expands Libs.private / Requires.private; + // it doesn't rewrite Libs: 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(b, deps); + return .{ .step = &lib.step, .output = lib.getEmittedBin(), .dsym = dsymutil, + .pkg_config = pcs.shared, + .pkg_config_static = pcs.static, }; } @@ -168,7 +173,7 @@ pub fn initMacOSUniversal( const universal = LipoStep.create(b, .{ .name = "ghostty", - .out_name = "libghostty.a", + .out_name = "ghostty-internal.a", .input_a = aarch64.output, .input_b = x86_64.output, }); @@ -180,13 +185,31 @@ pub fn initMacOSUniversal( // You can't run dsymutil on a universal binary, you have to // do it on the individual binaries. .dsym = null, + .pkg_config = null, + .pkg_config_static = null, }; } pub fn install(self: *const GhosttyLib, name: []const u8) void { const b = self.step.owner; + const step = b.getInstallStep(); const lib_install = b.addInstallLibFile(self.output, name); - b.getInstallStep().dependOn(&lib_install.step); + step.dependOn(&lib_install.step); + + if (self.pkg_config) |pc| { + step.dependOn(&b.addInstallFileWithDir( + pc, + .prefix, + "share/pkgconfig/ghostty-internal.pc", + ).step); + } + if (self.pkg_config_static) |pc| { + step.dependOn(&b.addInstallFileWithDir( + pc, + .prefix, + "share/pkgconfig/ghostty-internal-static.pc", + ).step); + } } pub fn installHeader(self: *const GhosttyLib) void { @@ -197,3 +220,61 @@ pub fn installHeader(self: *const GhosttyLib) void { ); b.getInstallStep().dependOn(&header_install.step); } + +const PkgConfigFiles = struct { + shared: std.Build.LazyPath, + static: std.Build.LazyPath, +}; + +fn pkgConfigFiles( + b: *std.Build, + deps: *const SharedDeps, +) PkgConfigFiles { + const os_tag = deps.config.target.result.os.tag; + const wf = b.addWriteFiles(); + + return .{ + .shared = wf.add("ghostty-internal.pc", b.fmt( + \\prefix={s} + \\includedir=${{prefix}}/include + \\libdir=${{prefix}}/lib + \\ + \\Name: ghostty-internal + \\URL: https://github.com/ghostty-org/ghostty + \\Description: Ghostty internal library (not for external use) + \\Version: {f} + \\Cflags: -I${{includedir}} + \\Libs: ${{libdir}}/{s} + \\Libs.private: + \\Requires.private: + , .{ b.install_prefix, deps.config.version, sharedLibraryName(os_tag) })), + .static = wf.add("ghostty-internal-static.pc", b.fmt( + \\prefix={s} + \\includedir=${{prefix}}/include + \\libdir=${{prefix}}/lib + \\ + \\Name: ghostty-internal-static + \\URL: https://github.com/ghostty-org/ghostty + \\Description: Ghostty internal library, static (not for external use) + \\Version: {f} + \\Cflags: -I${{includedir}} + \\Libs: ${{libdir}}/{s} + \\Libs.private: + \\Requires.private: + , .{ b.install_prefix, deps.config.version, staticLibraryName(os_tag) })), + }; +} + +fn sharedLibraryName(os_tag: std.Target.Os.Tag) []const u8 { + return if (os_tag == .windows) + "ghostty-internal.dll" + else + "ghostty-internal.so"; +} + +fn staticLibraryName(os_tag: std.Target.Os.Tag) []const u8 { + return if (os_tag == .windows) + "ghostty-internal-static.lib" + else + "ghostty-internal.a"; +} diff --git a/vendor/libghostty-vt/src/build/GhosttyLibVt.zig b/vendor/libghostty-vt/src/build/GhosttyLibVt.zig index 3e1be477..65f945bf 100644 --- a/vendor/libghostty-vt/src/build/GhosttyLibVt.zig +++ b/vendor/libghostty-vt/src/build/GhosttyLibVt.zig @@ -4,9 +4,12 @@ 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"); /// The step that generates the file. step: *std.Build.Step, @@ -21,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. @@ -40,7 +44,7 @@ pub fn initWasm( const exe = b.addExecutable(.{ .name = "ghostty-vt", .root_module = zig.vt_c, - .version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 }, + .version = zig.version, }); // Allow exported symbols to actually be exported. @@ -82,6 +86,7 @@ pub fn initWasm( .output = output, .dsym = null, .pkg_config = null, + .pkg_config_static = null, }; } @@ -99,6 +104,94 @@ pub fn initShared( return initLib(b, zig, .dynamic); } +/// Apple platform targets for xcframework slices. +pub const ApplePlatform = enum { + macos_universal, + ios, + ios_simulator, + // tvOS, watchOS, and visionOS are not yet supported by Zig's + // standard library (missing PATH_MAX, mcontext fields, etc.). + + /// Platforms that have device + simulator pairs, gated on SDK detection. + const sdk_platforms = [_]struct { + os_tag: std.Target.Os.Tag, + device: ApplePlatform, + simulator: ApplePlatform, + }{ + .{ .os_tag = .ios, .device = .ios, .simulator = .ios_simulator }, + }; +}; + +/// Static libraries for each Apple platform, keyed by `ApplePlatform`. +pub const AppleLibs = std.EnumMap(ApplePlatform, GhosttyLibVt); + +/// Build static libraries for all available Apple platforms. +/// Always builds a macOS universal (arm64 + x86_64) fat binary. +/// Additional platforms are included if their SDK is detected. +pub fn initStaticAppleUniversal( + b: *std.Build, + cfg: *const Config, + deps: *const SharedDeps, + zig: *const GhosttyZig, +) !AppleLibs { + var result: AppleLibs = .{}; + + // macOS universal (arm64 + x86_64) + const aarch64_zig = try zig.retarget( + b, + cfg, + deps, + Config.genericMacOSTarget(b, .aarch64), + ); + const x86_64_zig = try zig.retarget( + b, + cfg, + deps, + Config.genericMacOSTarget(b, .x86_64), + ); + const aarch64 = try initStatic(b, &aarch64_zig); + const x86_64 = try initStatic(b, &x86_64_zig); + const universal = LipoStep.create(b, .{ + .name = "ghostty-vt", + .out_name = "libghostty-vt.a", + .input_a = aarch64.output, + .input_b = x86_64.output, + }); + result.put(.macos_universal, .{ + .step = universal.step, + .artifact = universal.step, + .kind = .static, + .output = universal.output, + .dsym = null, + .pkg_config = null, + .pkg_config_static = null, + }); + + // Additional Apple platforms, each gated on SDK availability. + for (ApplePlatform.sdk_platforms) |p| { + const target_query: std.Target.Query = .{ + .cpu_arch = .aarch64, + .os_tag = p.os_tag, + .os_version_min = Config.osVersionMin(p.os_tag), + }; + if (detectAppleSDK(b.resolveTargetQuery(target_query).result)) { + const dev_zig = try zig.retarget(b, cfg, deps, b.resolveTargetQuery(target_query)); + result.put(p.device, try initStatic(b, &dev_zig)); + + const sim_zig = try zig.retarget(b, cfg, deps, b.resolveTargetQuery(.{ + .cpu_arch = .aarch64, + .os_tag = p.os_tag, + .os_version_min = Config.osVersionMin(p.os_tag), + .abi = .simulator, + .cpu_model = .{ .explicit = &std.Target.aarch64.cpu.apple_a17 }, + })); + result.put(p.simulator, try initStatic(b, &sim_zig)); + } + } + + return result; +} + fn initLib( b: *std.Build, zig: *const GhosttyZig, @@ -113,7 +206,7 @@ fn initLib( .name = if (kind == .static) "ghostty-vt-static" else "ghostty-vt", .linkage = linkage, .root_module = zig.vt_c, - .version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 }, + .version = zig.version, }); lib.installHeadersDirectory( b.path("include/ghostty"), @@ -174,36 +267,27 @@ 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: 0.1.0 - \\Cflags: -I${{includedir}} - \\Libs: -L${{libdir}} -lghostty-vt - \\Libs.private: {s} - \\Requires.private: {s} - , .{ b.install_prefix, 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 - // to link one file. Skip on Windows where ar/libtool aren't available. + // to link one file. if (kind == .static and - zig.simd_libs.items.len > 0 and - target.result.os.tag != .windows) + zig.simd_libs.items.len > 0) { 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); + const combined = CombineArchivesStep.create(b, target, "ghostty-vt", sources.items); combined.step.dependOn(&lib.step); return .{ @@ -212,7 +296,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, }; } @@ -222,64 +307,79 @@ 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, }; } -/// 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. +/// Vendored C++ dependencies are built in no-libcxx mode so consumers +/// don't need libc++. System-provided simdutf still requires it. fn libsPrivate( zig: *const GhosttyZig, ) []const u8 { 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 @@ -294,6 +394,59 @@ fn requiresPrivate(b: *std.Build) []const u8 { return ""; } +/// Create an XCFramework bundle from Apple platform static libraries. +pub fn xcframework( + apple_libs: *const AppleLibs, + b: *std.Build, +) *XCFrameworkStep { + // Generate a headers directory with a module map for Swift PM. + // We can't use include/ directly because it contains a module map + // for GhosttyKit (the macOS app library). + const wf = b.addWriteFiles(); + _ = wf.addCopyDirectory( + b.path("include/ghostty"), + "ghostty", + .{ .include_extensions = &.{".h"} }, + ); + _ = wf.add("module.modulemap", + \\module GhosttyVt { + \\ umbrella header "ghostty/vt.h" + \\ export * + \\} + \\ + ); + const headers = wf.getDirectory(); + + var libraries: [AppleLibs.len]XCFrameworkStep.Library = undefined; + var lib_count: usize = 0; + for (std.enums.values(ApplePlatform)) |platform| { + if (apple_libs.get(platform)) |lib| { + libraries[lib_count] = .{ + .library = lib.output, + .headers = headers, + .dsym = null, + }; + lib_count += 1; + } + } + + return XCFrameworkStep.create(b, .{ + .name = "ghostty-vt", + .out_path = b.pathJoin(&.{ b.install_prefix, "lib/ghostty-vt.xcframework" }), + .libraries = libraries[0..lib_count], + }); +} + +/// Returns true if the Apple SDK for the given target is installed. +fn detectAppleSDK(target: std.Target) bool { + _ = std.zig.LibCInstallation.findNative(.{ + .allocator = std.heap.page_allocator, + .target = &target, + .verbose = false, + }) catch return false; + return true; +} + pub fn install( self: *const GhosttyLibVt, step: *std.Build.Step, @@ -307,4 +460,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); + } } diff --git a/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig b/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig index 3afeb907..a19dd18a 100644 --- a/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig +++ b/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig @@ -53,6 +53,16 @@ pub fn init( }), )); + // Generate a headers directory with only ghostty.h and the module + // map. We can't use include/ directly because it also contains the + // libghostty-vt headers under include/ghostty/, which would trigger + // "umbrella header does not include header" warnings from Clang's + // module system. + const wf = b.addWriteFiles(); + _ = wf.addCopyFile(b.path("include/ghostty.h"), "ghostty.h"); + _ = wf.addCopyFile(b.path("include/module.modulemap"), "module.modulemap"); + const headers = wf.getDirectory(); + // The xcframework wraps our ghostty library so that we can link // it to the final app built with Swift. const xcframework = XCFrameworkStep.create(b, .{ @@ -62,24 +72,24 @@ pub fn init( .universal => &.{ .{ .library = macos_universal.output, - .headers = b.path("include"), + .headers = headers, .dsym = macos_universal.dsym, }, .{ .library = ios.output, - .headers = b.path("include"), + .headers = headers, .dsym = ios.dsym, }, .{ .library = ios_sim.output, - .headers = b.path("include"), + .headers = headers, .dsym = ios_sim.dsym, }, }, .native => &.{.{ .library = macos_native.output, - .headers = b.path("include"), + .headers = headers, .dsym = macos_native.dsym, }}, }, diff --git a/vendor/libghostty-vt/src/build/GhosttyZig.zig b/vendor/libghostty-vt/src/build/GhosttyZig.zig index 4901180d..44c300e1 100644 --- a/vendor/libghostty-vt/src/build/GhosttyZig.zig +++ b/vendor/libghostty-vt/src/build/GhosttyZig.zig @@ -11,6 +11,9 @@ const TerminalBuildOptions = @import("../terminal/build_options.zig").Options; vt: *std.Build.Module, vt_c: *std.Build.Module, +/// The libghostty-vt version +version: std.SemanticVersion, + /// 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 @@ -22,8 +25,46 @@ pub fn init( cfg: *const Config, deps: *const SharedDeps, ) !GhosttyZig { + return initInner(b, cfg, deps, "ghostty-vt", "ghostty-vt-c"); +} + +/// Create a new GhosttyZig with modules retargeted to a different +/// architecture. Used to produce universal (fat) binaries on macOS. +pub fn retarget( + self: *const GhosttyZig, + b: *std.Build, + cfg: *const Config, + deps: *const SharedDeps, + target: std.Build.ResolvedTarget, +) !GhosttyZig { + _ = self; + const retargeted_config = try b.allocator.create(Config); + retargeted_config.* = cfg.*; + retargeted_config.target = target; + + const retargeted_deps = try b.allocator.create(SharedDeps); + retargeted_deps.* = try deps.retarget(b, target); + + // Use unique module names to avoid collisions with the original target. + const arch_name = @tagName(target.result.cpu.arch); + return initInner( + b, + retargeted_config, + retargeted_deps, + b.fmt("ghostty-vt-{s}", .{arch_name}), + b.fmt("ghostty-vt-c-{s}", .{arch_name}), + ); +} + +fn initInner( + b: *std.Build, + cfg: *const Config, + deps: *const SharedDeps, + vt_name: []const u8, + vt_c_name: []const u8, +) !GhosttyZig { // Terminal module build options - var vt_options = cfg.terminalOptions(); + var vt_options = cfg.terminalOptions(.lib); vt_options.artifact = .lib; // We presently don't allow Oniguruma in our Zig module at all. // We should expose this as a build option in the future so we can @@ -34,7 +75,7 @@ pub fn init( return .{ .vt = try initVt( - "ghostty-vt", + vt_name, b, cfg, deps, @@ -43,7 +84,7 @@ pub fn init( ), .vt_c = try initVt( - "ghostty-vt-c", + vt_c_name, b, cfg, deps, @@ -55,6 +96,8 @@ pub fn init( &simd_libs, ), + .version = cfg.lib_version, + .simd_libs = simd_libs, }; } @@ -76,13 +119,14 @@ fn initVt( .target = cfg.target, .optimize = cfg.optimize, - // SIMD require libc/libcpp (both) but otherwise we don't care. - // On MSVC, we must not use linkLibCpp because Zig passes - // -nostdinc++ and adds its bundled libc++/libc++abi headers - // which conflict with MSVC's C++ runtime. The MSVC SDK dirs - // added via link_libc contain both C and C++ headers. + // SIMD requires libc. Vendored C++ dependencies are built with + // no-libcxx mode (HWY_NO_LIBCXX / SIMDUTF_NO_LIBCXX) so we + // don't need libcpp. System-provided simdutf headers still + // use C++ stdlib headers, so we need libcpp in that case. .link_libc = if (cfg.simd) true else null, - .link_libcpp = if (cfg.simd and cfg.target.result.abi != .msvc) true else null, + .link_libcpp = if (cfg.simd and + b.systemIntegrationOption("simdutf", .{}) and + cfg.target.result.abi != .msvc) true else null, }); vt.addOptions("build_options", general_options); vt_options.add(b, vt); diff --git a/vendor/libghostty-vt/src/build/GitVersion.zig b/vendor/libghostty-vt/src/build/GitVersion.zig index 8b368d2c..41cc7f84 100644 --- a/vendor/libghostty-vt/src/build/GitVersion.zig +++ b/vendor/libghostty-vt/src/build/GitVersion.zig @@ -29,11 +29,14 @@ pub fn detect(b: *std.Build) !Version { error.ExitCodeFailure => return error.GitNotRepository, else => return err, }; - // Replace any '/' with '-' as including slashes will mess up building - // the dist tarball - the tarball uses the branch as part of the - // name and including slashes means that the tarball will end up in - // subdirectories instead of where it's supposed to be. - std.mem.replaceScalar(u8, tmp, '/', '-'); + + // Replace characters that are not valid in semantic version + // pre-release identifiers (which only allow [0-9A-Za-z-]). + // Slashes would also mess up dist tarball paths. + for (tmp) |*c| { + if (!std.ascii.isAlphanumeric(c.*) and c.* != '-') c.* = '-'; + } + break :b tmp; }; diff --git a/vendor/libghostty-vt/src/build/SharedDeps.zig b/vendor/libghostty-vt/src/build/SharedDeps.zig index aa63c082..b68be92d 100644 --- a/vendor/libghostty-vt/src/build/SharedDeps.zig +++ b/vendor/libghostty-vt/src/build/SharedDeps.zig @@ -133,7 +133,7 @@ pub fn add( step.root_module.addOptions("build_options", self.options); // Every exe needs the terminal options - self.config.terminalOptions().add(b, step.root_module); + self.config.terminalOptions(.ghostty).add(b, step.root_module); // C imports for locale constants and functions { @@ -412,7 +412,16 @@ pub fn add( // C files step.linkLibC(); step.addIncludePath(b.path("src/stb")); - step.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} }); + // Disable ubsan for MSVC: Zig's ubsan runtime cannot be bundled + // on Windows (LNK4229), leaving __ubsan_handle_* unresolved when + // the static archive is consumed by an external linker. + step.addCSourceFiles(.{ + .files = &.{"src/stb/stb.c"}, + .flags = if (step.rootModuleTarget().abi == .msvc) + &.{ "-fno-sanitize=undefined", "-fno-sanitize-trap=undefined" } + else + &.{}, + }); if (step.rootModuleTarget().os.tag == .linux) { step.addIncludePath(b.path("src/apprt/gtk")); } @@ -753,6 +762,7 @@ pub fn addSimd( ) !void { const target = m.resolved_target.?; const optimize = m.optimize.?; + const system_highway = b.systemIntegrationOption("highway", .{ .default = false }); // Simdutf if (b.systemIntegrationOption("simdutf", .{})) { @@ -761,6 +771,7 @@ pub fn addSimd( if (b.lazyDependency("simdutf", .{ .target = target, .optimize = optimize, + .no_libcxx = true, })) |simdutf_dep| { m.linkLibrary(simdutf_dep.artifact("simdutf")); if (static_libs) |v| try v.append( @@ -771,7 +782,7 @@ pub fn addSimd( } // Highway - if (b.systemIntegrationOption("highway", .{ .default = false })) { + if (system_highway) { m.linkSystemLibrary("libhwy", dynamic_link_opts); } else { if (b.lazyDependency("highway", .{ @@ -786,18 +797,6 @@ pub fn addSimd( } } - // utfcpp - This is used as a dependency on our hand-written C++ code - if (b.lazyDependency("utfcpp", .{ - .target = target, - .optimize = optimize, - })) |utfcpp_dep| { - m.linkLibrary(utfcpp_dep.artifact("utfcpp")); - if (static_libs) |v| try v.append( - b.allocator, - utfcpp_dep.artifact("utfcpp").getEmittedBin(), - ); - } - // SIMD C++ files m.addIncludePath(b.path("src")); { @@ -830,10 +829,27 @@ pub fn addSimd( "-std=c++17", ); - // Disable ubsan for MSVC to avoid undefined references to - // __ubsan_handle_* symbols that require a runtime we don't link - // and bundle. Hopefully we can fix this one day since ubsan is nice! - if (target.result.abi == .msvc) try flags.appendSlice(b.allocator, &.{ + // Keep our SIMD sources in the same Highway header mode as the + // vendored package build so HWY's inline dispatch/runtime helpers + // have a consistent ABI. + if (!system_highway) try flags.append( + b.allocator, + "-DHWY_NO_LIBCXX", + ); + + // When using the vendored simdutf, build its headers in no-libcxx + // mode so we don't need C++ standard library headers at all. + // System simdutf headers may not support this define. + if (!b.systemIntegrationOption("simdutf", .{})) try flags.append( + b.allocator, + "-DSIMDUTF_NO_LIBCXX", + ); + + // Disable ubsan for Windows C/C++ objects to avoid undefined + // __ubsan_handle_* references. The Zig libraries on Windows don't + // currently bundle a matching UBSan runtime for these objects in + // our build configurations (this affects both MSVC and GNU ABIs). + if (target.result.os.tag == .windows) try flags.appendSlice(b.allocator, &.{ "-fno-sanitize=undefined", "-fno-sanitize-trap=undefined", }); diff --git a/vendor/libghostty-vt/src/build/combine_archives.zig b/vendor/libghostty-vt/src/build/combine_archives.zig new file mode 100644 index 00000000..04ec8e97 --- /dev/null +++ b/vendor/libghostty-vt/src/build/combine_archives.zig @@ -0,0 +1,55 @@ +//! Build tool that combines multiple static archives into a single fat +//! archive using an MRI script piped to `zig ar -M`. +//! +//! MRI scripts require stdin piping (`ar -M < script`), which can't be +//! expressed as a single command in the zig build system's RunStep. The +//! previous approach used `/bin/sh -c` to do the piping, but that isn't +//! available on Windows. This tool handles both the script generation +//! and the piping in a single cross-platform executable. +//! +//! Usage: combine_archives <zig_exe> <output.a> <input1.a> [input2.a ...] + +const std = @import("std"); + +pub fn main() !void { + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + const alloc = gpa.allocator(); + + const args = try std.process.argsAlloc(alloc); + if (args.len < 4) { + std.log.err("usage: combine_archives <zig_exe> <output> <input...>", .{}); + std.process.exit(1); + } + + const zig_exe = args[1]; + const output_path = args[2]; + const inputs = args[3..]; + + // Build the MRI script. + var script: std.ArrayListUnmanaged(u8) = .empty; + try script.appendSlice(alloc, "CREATE "); + try script.appendSlice(alloc, output_path); + try script.append(alloc, '\n'); + for (inputs) |input| { + try script.appendSlice(alloc, "ADDLIB "); + try script.appendSlice(alloc, input); + try script.append(alloc, '\n'); + } + try script.appendSlice(alloc, "SAVE\nEND\n"); + + var child: std.process.Child = .init(&.{ zig_exe, "ar", "-M" }, alloc); + child.stdin_behavior = .Pipe; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + try child.spawn(); + try child.stdin.?.writeAll(script.items); + child.stdin.?.close(); + child.stdin = null; + + const term = try child.wait(); + if (term.Exited != 0) { + std.log.err("zig ar -M exited with code {d}", .{term.Exited}); + std.process.exit(1); + } +} |
