diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/build/SharedDeps.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/build/SharedDeps.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/build/SharedDeps.zig | 974 |
1 files changed, 974 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/build/SharedDeps.zig b/vendor/libghostty-vt/src/build/SharedDeps.zig new file mode 100644 index 00000000..aa63c082 --- /dev/null +++ b/vendor/libghostty-vt/src/build/SharedDeps.zig @@ -0,0 +1,974 @@ +const SharedDeps = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); + +const Config = @import("Config.zig"); +const HelpStrings = @import("HelpStrings.zig"); +const MetallibStep = @import("MetallibStep.zig"); +const UnicodeTables = @import("UnicodeTables.zig"); +const GhosttyFrameData = @import("GhosttyFrameData.zig"); +const DistResource = @import("GhosttyDist.zig").Resource; + +config: *const Config, + +options: *std.Build.Step.Options, +help_strings: HelpStrings, +metallib: ?*MetallibStep, +unicode_tables: UnicodeTables, +framedata: GhosttyFrameData, +uucode_tables: std.Build.LazyPath, + +/// Used to keep track of a list of file sources. +pub const LazyPathList = std.ArrayList(std.Build.LazyPath); + +pub fn init(b: *std.Build, cfg: *const Config) !SharedDeps { + const uucode_tables = blk: { + const uucode = b.dependency("uucode", .{ + .build_config_path = b.path("src/build/uucode_config.zig"), + }); + + break :blk uucode.namedLazyPath("tables.zig"); + }; + + var result: SharedDeps = .{ + .config = cfg, + .help_strings = try .init(b, cfg), + .unicode_tables = try .init(b, uucode_tables), + .framedata = try .init(b), + .uucode_tables = uucode_tables, + + // Setup by retarget + .options = undefined, + .metallib = undefined, + }; + try result.initTarget(b, cfg.target); + if (cfg.emit_unicode_table_gen) result.unicode_tables.install(b); + return result; +} + +/// Retarget our dependencies for another build target. Modifies in-place. +pub fn retarget( + self: *const SharedDeps, + b: *std.Build, + target: std.Build.ResolvedTarget, +) !SharedDeps { + var result = self.*; + try result.initTarget(b, target); + return result; +} + +/// Change the exe entrypoint. +pub fn changeEntrypoint( + self: *const SharedDeps, + b: *std.Build, + entrypoint: Config.ExeEntrypoint, +) !SharedDeps { + // Change our config + const config = try b.allocator.create(Config); + config.* = self.config.*; + config.exe_entrypoint = entrypoint; + + var result = self.*; + result.config = config; + result.options = b.addOptions(); + try config.addOptions(result.options); + + return result; +} + +fn initTarget( + self: *SharedDeps, + b: *std.Build, + target: std.Build.ResolvedTarget, +) !void { + // Update our metallib + self.metallib = .create(b, .{ + .name = "Ghostty", + .target = target, + .sources = &.{b.path("src/renderer/shaders/shaders.metal")}, + }); + + // Change our config + const config = try b.allocator.create(Config); + config.* = self.config.*; + config.target = target; + self.config = config; + + // Setup our shared build options + self.options = b.addOptions(); + try self.config.addOptions(self.options); +} + +pub fn add( + self: *const SharedDeps, + step: *std.Build.Step.Compile, +) !LazyPathList { + const b = step.step.owner; + + // We could use our config.target/optimize fields here but its more + // correct to always match our step. + const target = step.root_module.resolved_target.?; + const optimize = step.root_module.optimize.?; + + // We maintain a list of our static libraries and return it so that + // we can build a single fat static library for the final app. + var static_libs: LazyPathList = .empty; + errdefer static_libs.deinit(b.allocator); + + // WARNING: This is a hack! + // If we're cross-compiling to Darwin then we don't add any deps. + // We don't support cross-compiling to Darwin but due to the way + // lazy dependencies work with Zig, we call this function. So we just + // bail. The build will fail but the build would've failed anyways. + // And this lets other non-platform-specific targets like `-Demit-lib-vt` + // cross-compile properly. + if (!builtin.target.os.tag.isDarwin() and + self.config.target.result.os.tag.isDarwin()) + { + return static_libs; + } + + // Every exe gets build options populated + step.root_module.addOptions("build_options", self.options); + + // Every exe needs the terminal options + self.config.terminalOptions().add(b, step.root_module); + + // C imports for locale constants and functions + { + const c = b.addTranslateC(.{ + .root_source_file = b.path("src/os/locale.c"), + .target = target, + .optimize = optimize, + }); + if (target.result.os.tag.isDarwin()) { + const libc = try std.zig.LibCInstallation.findNative(.{ + .allocator = b.allocator, + .target = &target.result, + .verbose = false, + }); + c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? }); + } + step.root_module.addImport("locale-c", c.createModule()); + } + + // C imports needed to manage/create PTYs + switch (target.result.os.tag) { + .freebsd, + .linux, + .macos, + => { + const c = b.addTranslateC(.{ + .root_source_file = b.path("src/pty.c"), + .target = target, + .optimize = optimize, + }); + switch (target.result.os.tag) { + .macos => { + const libc = try std.zig.LibCInstallation.findNative(.{ + .allocator = b.allocator, + .target = &target.result, + .verbose = false, + }); + c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? }); + }, + else => {}, + } + step.root_module.addImport("pty-c", c.createModule()); + }, + else => {}, + } + + // Freetype. We always include this even if our font backend doesn't + // use it because Dear Imgui uses Freetype. + _ = b.systemIntegrationOption("freetype", .{}); // Shows it in help + if (b.lazyDependency("freetype", .{ + .target = target, + .optimize = optimize, + .@"enable-libpng" = true, + })) |freetype_dep| { + step.root_module.addImport( + "freetype", + freetype_dep.module("freetype"), + ); + + if (b.systemIntegrationOption("freetype", .{})) { + step.linkSystemLibrary2("bzip2", dynamic_link_opts); + step.linkSystemLibrary2("freetype2", dynamic_link_opts); + } else { + step.linkLibrary(freetype_dep.artifact("freetype")); + try static_libs.append( + b.allocator, + freetype_dep.artifact("freetype").getEmittedBin(), + ); + } + } + + // Harfbuzz + _ = b.systemIntegrationOption("harfbuzz", .{}); // Shows it in help + if (self.config.font_backend.hasHarfbuzz()) { + if (b.lazyDependency("harfbuzz", .{ + .target = target, + .optimize = optimize, + .@"enable-freetype" = self.config.font_backend.hasFreetype(), + .@"enable-coretext" = self.config.font_backend.hasCoretext(), + })) |harfbuzz_dep| { + step.root_module.addImport( + "harfbuzz", + harfbuzz_dep.module("harfbuzz"), + ); + if (b.systemIntegrationOption("harfbuzz", .{})) { + step.linkSystemLibrary2("harfbuzz", dynamic_link_opts); + } else { + step.linkLibrary(harfbuzz_dep.artifact("harfbuzz")); + try static_libs.append( + b.allocator, + harfbuzz_dep.artifact("harfbuzz").getEmittedBin(), + ); + } + } + } + + // Fontconfig + _ = b.systemIntegrationOption("fontconfig", .{}); // Shows it in help + if (self.config.font_backend.hasFontconfig()) { + if (b.lazyDependency("fontconfig", .{ + .target = target, + .optimize = optimize, + })) |fontconfig_dep| { + step.root_module.addImport( + "fontconfig", + fontconfig_dep.module("fontconfig"), + ); + + if (b.systemIntegrationOption("fontconfig", .{})) { + step.linkSystemLibrary2("fontconfig", dynamic_link_opts); + } else { + step.linkLibrary(fontconfig_dep.artifact("fontconfig")); + try static_libs.append( + b.allocator, + fontconfig_dep.artifact("fontconfig").getEmittedBin(), + ); + } + } + } + + // Libpng - Ghostty doesn't actually use this directly, its only used + // through dependencies, so we only need to add it to our static + // libs list if we're not using system integration. The dependencies + // will handle linking it. + if (!b.systemIntegrationOption("libpng", .{})) { + if (b.lazyDependency("libpng", .{ + .target = target, + .optimize = optimize, + })) |libpng_dep| { + step.linkLibrary(libpng_dep.artifact("png")); + try static_libs.append( + b.allocator, + libpng_dep.artifact("png").getEmittedBin(), + ); + } + } + + // Zlib - same as libpng, only used through dependencies. + if (!b.systemIntegrationOption("zlib", .{})) { + if (b.lazyDependency("zlib", .{ + .target = target, + .optimize = optimize, + })) |zlib_dep| { + step.linkLibrary(zlib_dep.artifact("z")); + try static_libs.append( + b.allocator, + zlib_dep.artifact("z").getEmittedBin(), + ); + } + } + + // Oniguruma + if (b.lazyDependency("oniguruma", .{ + .target = target, + .optimize = optimize, + })) |oniguruma_dep| { + step.root_module.addImport( + "oniguruma", + oniguruma_dep.module("oniguruma"), + ); + if (b.systemIntegrationOption("oniguruma", .{})) { + step.linkSystemLibrary2("oniguruma", dynamic_link_opts); + } else { + step.linkLibrary(oniguruma_dep.artifact("oniguruma")); + try static_libs.append( + b.allocator, + oniguruma_dep.artifact("oniguruma").getEmittedBin(), + ); + } + } + + // Glslang + if (b.lazyDependency("glslang", .{ + .target = target, + .optimize = optimize, + })) |glslang_dep| { + step.root_module.addImport("glslang", glslang_dep.module("glslang")); + if (b.systemIntegrationOption("glslang", .{})) { + step.linkSystemLibrary2("glslang", dynamic_link_opts); + step.linkSystemLibrary2( + "glslang-default-resource-limits", + dynamic_link_opts, + ); + } else { + step.linkLibrary(glslang_dep.artifact("glslang")); + try static_libs.append( + b.allocator, + glslang_dep.artifact("glslang").getEmittedBin(), + ); + } + } + + // Spirv-cross + if (b.lazyDependency("spirv_cross", .{ + .target = target, + .optimize = optimize, + })) |spirv_cross_dep| { + step.root_module.addImport( + "spirv_cross", + spirv_cross_dep.module("spirv_cross"), + ); + if (b.systemIntegrationOption("spirv-cross", .{})) { + step.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts); + } else { + step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); + try static_libs.append( + b.allocator, + spirv_cross_dep.artifact("spirv_cross").getEmittedBin(), + ); + } + } + + // Sentry + if (self.config.sentry) { + if (b.lazyDependency("sentry", .{ + .target = target, + .optimize = optimize, + .backend = .breakpad, + })) |sentry_dep| { + step.root_module.addImport( + "sentry", + sentry_dep.module("sentry"), + ); + step.linkLibrary(sentry_dep.artifact("sentry")); + try static_libs.append( + b.allocator, + sentry_dep.artifact("sentry").getEmittedBin(), + ); + + // We also need to include breakpad in the static libs. + if (sentry_dep.builder.lazyDependency("breakpad", .{ + .target = target, + .optimize = optimize, + })) |breakpad_dep| { + try static_libs.append( + b.allocator, + breakpad_dep.artifact("breakpad").getEmittedBin(), + ); + } + } + } + + // Simd + if (self.config.simd) try addSimd( + b, + step.root_module, + &static_libs, + ); + + // Wasm we do manually since it is such a different build. + if (step.rootModuleTarget().cpu.arch == .wasm32) { + if (b.lazyDependency("zig_js", .{ + .target = target, + .optimize = optimize, + })) |js_dep| { + step.root_module.addImport( + "zig-js", + js_dep.module("zig-js"), + ); + } + + return static_libs; + } + + // On Linux, we need to add a couple common library paths that aren't + // on the standard search list. i.e. GTK is often in /usr/lib/x86_64-linux-gnu + // on x86_64. + if (step.rootModuleTarget().os.tag == .linux) { + const triple = try step.rootModuleTarget().linuxTriple(b.allocator); + const path = b.fmt("/usr/lib/{s}", .{triple}); + if (std.fs.accessAbsolute(path, .{})) { + step.addLibraryPath(.{ .cwd_relative = path }); + } else |_| {} + } + + // C files + step.linkLibC(); + step.addIncludePath(b.path("src/stb")); + step.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} }); + if (step.rootModuleTarget().os.tag == .linux) { + step.addIncludePath(b.path("src/apprt/gtk")); + } + + // libcpp is required for various dependencies. On MSVC, we must + // not use linkLibCpp because Zig unconditionally passes -nostdinc++ + // and then adds its bundled libc++/libc++abi include paths, which + // conflict with MSVC's own C++ runtime headers. The MSVC SDK + // include directories (already added via linkLibC above) contain + // both C and C++ headers, so linkLibCpp is not needed. + if (step.rootModuleTarget().abi != .msvc) { + step.linkLibCpp(); + } + + // We always require the system SDK so that our system headers are available. + // This makes things like `os/log.h` available for cross-compiling. + if (step.rootModuleTarget().os.tag.isDarwin()) { + try @import("apple_sdk").addPaths(b, step); + + const metallib = self.metallib.?; + metallib.output.addStepDependencies(&step.step); + step.root_module.addAnonymousImport("ghostty_metallib", .{ + .root_source_file = metallib.output, + }); + } + + // Other dependencies, mostly pure Zig + if (b.lazyDependency("opengl", .{})) |dep| { + step.root_module.addImport("opengl", dep.module("opengl")); + } + if (b.lazyDependency("vaxis", .{})) |dep| { + step.root_module.addImport("vaxis", dep.module("vaxis")); + } + if (b.lazyDependency("wuffs", .{ + .target = target, + .optimize = optimize, + })) |dep| { + step.root_module.addImport("wuffs", dep.module("wuffs")); + } + if (b.lazyDependency("libxev", .{ + .target = target, + .optimize = optimize, + })) |dep| { + step.root_module.addImport("xev", dep.module("xev")); + } + if (b.lazyDependency("z2d", .{ + .target = target, + .optimize = optimize, + })) |dep| { + step.root_module.addImport("z2d", dep.module("z2d")); + } + self.addUucode(b, step.root_module, target, optimize); + if (b.lazyDependency("zf", .{ + .target = target, + .optimize = optimize, + .with_tui = false, + })) |dep| { + step.root_module.addImport("zf", dep.module("zf")); + } + + // Mac Stuff + if (step.rootModuleTarget().os.tag.isDarwin()) { + if (b.lazyDependency("zig_objc", .{ + .target = target, + .optimize = optimize, + })) |objc_dep| { + step.root_module.addImport( + "objc", + objc_dep.module("objc"), + ); + } + + if (b.lazyDependency("macos", .{ + .target = target, + .optimize = optimize, + })) |macos_dep| { + step.root_module.addImport( + "macos", + macos_dep.module("macos"), + ); + step.linkLibrary( + macos_dep.artifact("macos"), + ); + try static_libs.append( + b.allocator, + macos_dep.artifact("macos").getEmittedBin(), + ); + } + + if (self.config.renderer == .opengl) { + step.linkFramework("OpenGL"); + } + + // Apple platforms do not include libc libintl so we bundle it. + // This is LGPL but since our source code is open source we are + // in compliance with the LGPL since end users can modify this + // build script to replace the bundled libintl with their own. + if (b.lazyDependency("libintl", .{ + .target = target, + .optimize = optimize, + })) |libintl_dep| { + step.linkLibrary(libintl_dep.artifact("intl")); + try static_libs.append( + b.allocator, + libintl_dep.artifact("intl").getEmittedBin(), + ); + } + } + + // cimgui + if (b.lazyDependency("dcimgui", .{ + .target = target, + .optimize = optimize, + .freetype = true, + .@"backend-metal" = target.result.os.tag.isDarwin(), + .@"backend-osx" = target.result.os.tag == .macos, + // OpenGL3 backend should only be built on non-Apple targets. + // Apple platforms use Metal (and macOS may also use the OSX backend). + .@"backend-opengl3" = !target.result.os.tag.isDarwin(), + })) |dep| { + step.root_module.addImport("dcimgui", dep.module("dcimgui")); + step.linkLibrary(dep.artifact("dcimgui")); + try static_libs.append( + b.allocator, + dep.artifact("dcimgui").getEmittedBin(), + ); + } + + // Fonts + { + // JetBrains Mono + if (b.lazyDependency("jetbrains_mono", .{})) |jb_mono| { + step.root_module.addAnonymousImport( + "jetbrains_mono_regular", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Regular.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_bold", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Bold.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_italic", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Italic.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_bold_italic", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-BoldItalic.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_variable", + .{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono[wght].ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_variable_italic", + .{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono-Italic[wght].ttf") }, + ); + } + + // Symbols-only nerd font + if (b.lazyDependency("nerd_fonts_symbols_only", .{})) |nf_symbols| { + step.root_module.addAnonymousImport( + "nerd_fonts_symbols_only", + .{ .root_source_file = nf_symbols.path("SymbolsNerdFont-Regular.ttf") }, + ); + } + } + + // If we're building an exe then we have additional dependencies. + if (step.kind != .lib) { + // We always statically compile glad + step.addIncludePath(b.path("vendor/glad/include/")); + step.addCSourceFile(.{ + .file = b.path("vendor/glad/src/gl.c"), + .flags = &.{}, + }); + + // When we're targeting flatpak we ALWAYS link GTK so we + // get access to glib for dbus. + if (self.config.flatpak) step.linkSystemLibrary2("gtk4", dynamic_link_opts); + + switch (self.config.app_runtime) { + .none => {}, + .gtk => try self.addGtkNg(step), + } + } + + self.help_strings.addImport(step); + self.unicode_tables.addImport(step); + self.framedata.addImport(step); + + return static_libs; +} + +/// Setup the dependencies for the GTK apprt build. +fn addGtkNg( + self: *const SharedDeps, + step: *std.Build.Step.Compile, +) !void { + const b = step.step.owner; + const target = step.root_module.resolved_target.?; + const optimize = step.root_module.optimize.?; + + const gobject_ = b.lazyDependency("gobject", .{ + .target = target, + .optimize = optimize, + }); + if (gobject_) |gobject| { + const gobject_imports = .{ + .{ "adw", "adw1" }, + .{ "gdk", "gdk4" }, + .{ "gio", "gio2" }, + .{ "glib", "glib2" }, + .{ "gobject", "gobject2" }, + .{ "gtk", "gtk4" }, + .{ "xlib", "xlib2" }, + }; + inline for (gobject_imports) |import| { + const name, const module = import; + step.root_module.addImport(name, gobject.module(module)); + } + } + + step.linkSystemLibrary2("gtk4", dynamic_link_opts); + step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + + if (self.config.x11) { + step.linkSystemLibrary2("X11", dynamic_link_opts); + if (gobject_) |gobject| { + step.root_module.addImport( + "gdk_x11", + gobject.module("gdkx114"), + ); + } + } + + if (self.config.wayland) wayland: { + // These need to be all be called to note that we need them. + const wayland_dep_ = b.lazyDependency("wayland", .{}); + const wayland_protocols_dep_ = b.lazyDependency( + "wayland_protocols", + .{}, + ); + const plasma_wayland_protocols_dep_ = b.lazyDependency( + "plasma_wayland_protocols", + .{}, + ); + const zig_wayland_import_ = b.lazyImport( + @import("../../build.zig"), + "zig_wayland", + ); + const zig_wayland_dep_ = b.lazyDependency("zig_wayland", .{}); + + // Unwrap or return, there are no more dependencies below. + const wayland_dep = wayland_dep_ orelse break :wayland; + const wayland_protocols_dep = wayland_protocols_dep_ orelse break :wayland; + const plasma_wayland_protocols_dep = plasma_wayland_protocols_dep_ orelse break :wayland; + const zig_wayland_import = zig_wayland_import_ orelse break :wayland; + const zig_wayland_dep = zig_wayland_dep_ orelse break :wayland; + + const Scanner = zig_wayland_import.Scanner; + const scanner = Scanner.create(zig_wayland_dep.builder, .{ + .wayland_xml = wayland_dep.path("protocol/wayland.xml"), + .wayland_protocols = wayland_protocols_dep.path(""), + }); + + // FIXME: replace with `zxdg_decoration_v1` once GTK merges https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6398 + scanner.addCustomProtocol( + plasma_wayland_protocols_dep.path("src/protocols/server-decoration.xml"), + ); + scanner.addCustomProtocol( + plasma_wayland_protocols_dep.path("src/protocols/slide.xml"), + ); + scanner.addCustomProtocol( + plasma_wayland_protocols_dep.path("src/protocols/kde-output-order-v1.xml"), + ); + scanner.addSystemProtocol("staging/xdg-activation/xdg-activation-v1.xml"); + scanner.addSystemProtocol("staging/ext-background-effect/ext-background-effect-v1.xml"); + + scanner.generate("wl_compositor", 1); + scanner.generate("org_kde_kwin_server_decoration_manager", 1); + scanner.generate("org_kde_kwin_slide_manager", 1); + scanner.generate("kde_output_order_v1", 1); + scanner.generate("xdg_activation_v1", 1); + scanner.generate("ext_background_effect_manager_v1", 1); + + step.root_module.addImport("wayland", b.createModule(.{ + .root_source_file = scanner.result, + })); + if (gobject_) |gobject| step.root_module.addImport( + "gdk_wayland", + gobject.module("gdkwayland4"), + ); + + if (b.lazyDependency("gtk4_layer_shell", .{ + .target = target, + .optimize = optimize, + })) |gtk4_layer_shell| { + const layer_shell_module = gtk4_layer_shell.module("gtk4-layer-shell"); + if (gobject_) |gobject| { + layer_shell_module.addImport("gtk", gobject.module("gtk4")); + layer_shell_module.addImport("gdk", gobject.module("gdk4")); + } + step.root_module.addImport( + "gtk4-layer-shell", + layer_shell_module, + ); + + // IMPORTANT: gtk4-layer-shell must be linked BEFORE + // wayland-client, as it relies on shimming libwayland's APIs. + if (b.systemIntegrationOption("gtk4-layer-shell", .{})) { + step.linkSystemLibrary2("gtk4-layer-shell-0", dynamic_link_opts); + } else { + // gtk4-layer-shell *must* be dynamically linked, + // so we don't add it as a static library + const shared_lib = gtk4_layer_shell.artifact("gtk4-layer-shell"); + b.installArtifact(shared_lib); + step.linkLibrary(shared_lib); + } + } + + step.linkSystemLibrary2("wayland-client", dynamic_link_opts); + } + + { + // Get our gresource c/h files and add them to our build. + const dist = gtkNgDistResources(b); + step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} }); + step.addIncludePath(dist.resources_h.path(b).dirname()); + } +} + +/// Add only the dependencies required for `Config.simd` enabled. This also +/// adds all the simd source files for compilation. +pub fn addSimd( + b: *std.Build, + m: *std.Build.Module, + static_libs: ?*LazyPathList, +) !void { + const target = m.resolved_target.?; + const optimize = m.optimize.?; + + // Simdutf + if (b.systemIntegrationOption("simdutf", .{})) { + m.linkSystemLibrary("simdutf", dynamic_link_opts); + } else { + if (b.lazyDependency("simdutf", .{ + .target = target, + .optimize = optimize, + })) |simdutf_dep| { + m.linkLibrary(simdutf_dep.artifact("simdutf")); + if (static_libs) |v| try v.append( + b.allocator, + simdutf_dep.artifact("simdutf").getEmittedBin(), + ); + } + } + + // Highway + if (b.systemIntegrationOption("highway", .{ .default = false })) { + m.linkSystemLibrary("libhwy", dynamic_link_opts); + } else { + if (b.lazyDependency("highway", .{ + .target = target, + .optimize = optimize, + })) |highway_dep| { + m.linkLibrary(highway_dep.artifact("highway")); + if (static_libs) |v| try v.append( + b.allocator, + highway_dep.artifact("highway").getEmittedBin(), + ); + } + } + + // 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")); + { + // From hwy/detect_targets.h + const HWY_AVX10_2: c_int = 1 << 3; + const HWY_AVX3_SPR: c_int = 1 << 4; + const HWY_AVX3_ZEN4: c_int = 1 << 6; + const HWY_AVX3_DL: c_int = 1 << 7; + const HWY_AVX3: c_int = 1 << 8; + + var flags: std.ArrayListUnmanaged([]const u8) = .empty; + + // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414 + // To workaround this we just disable AVX512 support completely. + // The performance difference between AVX2 and AVX512 is not + // significant for our use case and AVX512 is very rare on consumer + // hardware anyways. + const HWY_DISABLED_TARGETS: c_int = HWY_AVX10_2 | HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3; + if (target.result.cpu.arch == .x86_64) try flags.append( + b.allocator, + b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}), + ); + + // MSVC requires explicit std specification otherwise these + // are guarded, at least on Windows 2025. Doing it unconditionally + // doesn't cause any issues on other platforms and ensures we get + // C++17 support on MSVC. + try flags.append( + b.allocator, + "-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, &.{ + "-fno-sanitize=undefined", + "-fno-sanitize-trap=undefined", + }); + + m.addCSourceFiles(.{ + .files = &.{ + "src/simd/base64.cpp", + "src/simd/codepoint_width.cpp", + "src/simd/index_of.cpp", + "src/simd/vt.cpp", + }, + .flags = flags.items, + }); + } +} + +/// Creates the resources that can be prebuilt for our dist build. +pub fn gtkNgDistResources( + b: *std.Build, +) struct { + resources_c: DistResource, + resources_h: DistResource, +} { + const gresource = @import("../apprt/gtk/build/gresource.zig"); + const gresource_xml = gresource_xml: { + const xml_exe = b.addExecutable(.{ + .name = "generate_gresource_xml", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/apprt/gtk/build/gresource.zig"), + .target = b.graph.host, + }), + }); + const xml_run = b.addRunArtifact(xml_exe); + + // Run our blueprint compiler across all of our blueprint files. + const blueprint_exe = b.addExecutable(.{ + .name = "gtk_blueprint_compiler", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/apprt/gtk/build/blueprint.zig"), + .target = b.graph.host, + }), + }); + blueprint_exe.linkLibC(); + blueprint_exe.linkSystemLibrary2("gtk4", dynamic_link_opts); + blueprint_exe.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + + for (gresource.blueprints) |bp| { + const blueprint_run = b.addRunArtifact(blueprint_exe); + blueprint_run.addArgs(&.{ + b.fmt("{d}", .{bp.major}), + b.fmt("{d}", .{bp.minor}), + }); + const ui_file = blueprint_run.addOutputFileArg(b.fmt( + "{d}.{d}/{s}.ui", + .{ + bp.major, + bp.minor, + bp.name, + }, + )); + blueprint_run.addFileArg(b.path(b.fmt( + "{s}/{d}.{d}/{s}.blp", + .{ + gresource.ui_path, + bp.major, + bp.minor, + bp.name, + }, + ))); + + xml_run.addFileArg(ui_file); + } + + break :gresource_xml xml_run.captureStdOut(); + }; + + const generate_c = b.addSystemCommand(&.{ + "glib-compile-resources", + "--c-name", + "ghostty", + "--generate-source", + "--target", + }); + const resources_c = generate_c.addOutputFileArg("ghostty_resources.c"); + generate_c.addFileArg(gresource_xml); + for (gresource.file_inputs) |path| { + generate_c.addFileInput(b.path(path)); + } + + const generate_h = b.addSystemCommand(&.{ + "glib-compile-resources", + "--c-name", + "ghostty", + "--generate-header", + "--target", + }); + const resources_h = generate_h.addOutputFileArg("ghostty_resources.h"); + generate_h.addFileArg(gresource_xml); + for (gresource.file_inputs) |path| { + generate_h.addFileInput(b.path(path)); + } + + return .{ + .resources_c = .{ + .dist = "src/apprt/gtk/ghostty_resources.c", + .generated = resources_c, + }, + .resources_h = .{ + .dist = "src/apprt/gtk/ghostty_resources.h", + .generated = resources_h, + }, + }; +} + +pub fn addUucode( + self: *const SharedDeps, + b: *std.Build, + module: *std.Build.Module, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, +) void { + if (b.lazyDependency("uucode", .{ + .target = target, + .optimize = optimize, + .tables_path = self.uucode_tables, + .build_config_path = b.path("src/build/uucode_config.zig"), + })) |dep| { + module.addImport("uucode", dep.module("uucode")); + } +} + +// For dynamic linking, we prefer dynamic linking and to search by +// mode first. Mode first will search all paths for a dynamic library +// before falling back to static. +const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, +}; |
