mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-31 20:59:02 +00:00
This commit represents the majority of the work necessary to upgrade Ghostty to use Zig 0.16.0. Key parts: * In addition to its previous responsibilities, the global state now houses state for global I/O implementations and the process environment. It is now also utilized in the main application along with the C library. Where necessary, global state is isolated from key parts of the implementation (e.g., in libghostty subsystems), and it's expected that this list will grow. * We currently manage our own C translation layer where necessary. In these cases, cImport has been removed in favor of the new external translate-c package. Due to fixes that have needed be made to properly translate the dependencies that were swapped out, as mentioned, we have had to backport fixes from the current translate-c package (and the upstream Arocc dependency). We will host this ourselves until Zig 0.17.0 is released with these fixes. * Where necessary (only a small number of cases), some stdlib code from 0.15.2 (and even from 0.17.0) has been taken, adopted, and vendored in lib/compat. Co-authored-by: Leah Amelia Chen <hi@pluie.me>
360 lines
13 KiB
Zig
360 lines
13 KiB
Zig
const std = @import("std");
|
|
const apple_sdk = @import("apple_sdk");
|
|
|
|
const root_build_container = @This();
|
|
|
|
pub fn build(b: *std.Build) !void {
|
|
const target = b.standardTargetOptions(.{});
|
|
const optimize = b.standardOptimizeOption(.{});
|
|
|
|
const coretext_enabled = b.option(bool, "enable-coretext", "Build coretext") orelse false;
|
|
const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
|
|
|
|
// 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,
|
|
};
|
|
|
|
const freetype = b.dependency("freetype", .{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.@"enable-libpng" = true,
|
|
});
|
|
|
|
const module = harfbuzz: {
|
|
const module = b.addModule("harfbuzz", .{
|
|
.root_source_file = b.path("main.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.imports = if (target.result.os.tag.isDarwin())
|
|
&.{
|
|
.{ .name = "freetype", .module = freetype.module("freetype") },
|
|
.{
|
|
.name = "macos",
|
|
.module = b.dependency("macos", .{ .target = target, .optimize = optimize })
|
|
.module("macos"),
|
|
},
|
|
}
|
|
else
|
|
&.{
|
|
.{ .name = "freetype", .module = freetype.module("freetype") },
|
|
},
|
|
});
|
|
|
|
try HarfBuzzC.addImportToModule(b, module, .{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.harfbuzz = if (b.systemIntegrationOption("harfbuzz", .{})) .{ .dynamic = dynamic_link_opts } else .static,
|
|
.coretext = coretext_enabled,
|
|
.freetype = if (freetype_enabled) ft: {
|
|
break :ft if (b.systemIntegrationOption("freetype", .{}))
|
|
.{ .dynamic = dynamic_link_opts }
|
|
else
|
|
.static;
|
|
} else null,
|
|
});
|
|
|
|
const options = b.addOptions();
|
|
options.addOption(bool, "coretext", coretext_enabled);
|
|
options.addOption(bool, "freetype", freetype_enabled);
|
|
module.addOptions("build_options", options);
|
|
break :harfbuzz module;
|
|
};
|
|
|
|
const test_exe = b.addTest(.{
|
|
.name = "test",
|
|
.root_module = module,
|
|
});
|
|
|
|
const tests_run = b.addRunArtifact(test_exe);
|
|
const test_step = b.step("test", "Run tests");
|
|
test_step.dependOn(&tests_run.step);
|
|
|
|
if (!b.systemIntegrationOption("harfbuzz", .{})) {
|
|
const lib = try buildLib(b, .{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
|
|
.coretext_enabled = coretext_enabled,
|
|
.freetype_enabled = freetype_enabled,
|
|
|
|
.dynamic_link_opts = dynamic_link_opts,
|
|
});
|
|
|
|
test_exe.root_module.linkLibrary(lib);
|
|
}
|
|
}
|
|
|
|
fn buildLib(b: *std.Build, options: anytype) !*std.Build.Step.Compile {
|
|
const target = options.target;
|
|
const optimize = options.optimize;
|
|
|
|
const coretext_enabled = options.coretext_enabled;
|
|
const freetype_enabled = options.freetype_enabled;
|
|
|
|
const freetype = b.dependency("freetype", .{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.@"enable-libpng" = true,
|
|
});
|
|
|
|
const lib = b.addLibrary(.{
|
|
.name = "harfbuzz",
|
|
.root_module = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
// 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 (added via linkLibC)
|
|
// contain both C and C++ headers, so linkLibCpp is not needed.
|
|
.link_libcpp = target.result.abi != .msvc,
|
|
}),
|
|
.linkage = .static,
|
|
});
|
|
|
|
if (target.result.os.tag.isDarwin()) {
|
|
try apple_sdk.addPaths(b, lib);
|
|
}
|
|
|
|
const dynamic_link_opts = options.dynamic_link_opts;
|
|
|
|
var flags: std.ArrayList([]const u8) = .empty;
|
|
defer flags.deinit(b.allocator);
|
|
try flags.appendSlice(b.allocator, &.{
|
|
"-DHAVE_STDBOOL_H",
|
|
});
|
|
// 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.
|
|
if (target.result.abi == .msvc) {
|
|
try flags.appendSlice(b.allocator, &.{
|
|
"-fno-sanitize=undefined",
|
|
"-fno-sanitize-trap=undefined",
|
|
});
|
|
}
|
|
if (target.result.os.tag != .windows) {
|
|
try flags.appendSlice(b.allocator, &.{
|
|
"-DHAVE_UNISTD_H",
|
|
"-DHAVE_SYS_MMAN_H",
|
|
"-DHAVE_PTHREAD=1",
|
|
});
|
|
}
|
|
|
|
// Freetype
|
|
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
|
|
if (freetype_enabled) {
|
|
try flags.appendSlice(b.allocator, &.{
|
|
"-DHAVE_FREETYPE=1",
|
|
|
|
// Let's just assume a new freetype
|
|
"-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1",
|
|
"-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1",
|
|
"-DHAVE_FT_DONE_MM_VAR=1",
|
|
"-DHAVE_FT_GET_TRANSFORM=1",
|
|
});
|
|
|
|
if (b.systemIntegrationOption("freetype", .{})) {
|
|
lib.root_module.linkSystemLibrary("freetype2", dynamic_link_opts);
|
|
} else {
|
|
lib.root_module.linkLibrary(freetype.artifact("freetype"));
|
|
}
|
|
}
|
|
|
|
if (coretext_enabled) {
|
|
try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"});
|
|
lib.root_module.linkFramework("CoreText", .{});
|
|
}
|
|
|
|
if (b.lazyDependency("harfbuzz", .{})) |upstream| {
|
|
lib.root_module.addIncludePath(upstream.path("src"));
|
|
lib.root_module.addCSourceFile(.{
|
|
.file = upstream.path("src/harfbuzz.cc"),
|
|
.flags = flags.items,
|
|
});
|
|
lib.installHeadersDirectory(
|
|
upstream.path("src"),
|
|
"",
|
|
.{ .include_extensions = &.{".h"} },
|
|
);
|
|
}
|
|
|
|
b.installArtifact(lib);
|
|
|
|
return lib;
|
|
}
|
|
|
|
const HarfBuzzC = struct {
|
|
const AddImportToModuleOptions = struct {
|
|
const LinkMode = union(enum) {
|
|
static,
|
|
dynamic: std.Build.Module.LinkSystemLibraryOptions,
|
|
};
|
|
|
|
target: std.Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
harfbuzz: LinkMode,
|
|
coretext: bool,
|
|
freetype: ?LinkMode,
|
|
};
|
|
|
|
fn fmtInclude(w: *std.Io.Writer, name: []const u8, mode: AddImportToModuleOptions.LinkMode) !void {
|
|
if (mode == .dynamic) {
|
|
try w.print("#include <{s}>\n", .{name});
|
|
} else {
|
|
try w.print("#include \"{s}\"\n", .{name});
|
|
}
|
|
}
|
|
|
|
fn addImportToModule(
|
|
b: *std.Build,
|
|
module: *std.Build.Module,
|
|
options: AddImportToModuleOptions,
|
|
) !void {
|
|
// TODO: There's a decent amount of duplication here right now.
|
|
// Basically we want to mirror what we're passing in buildLib to make
|
|
// sure that the translation is generated as correct as possible.
|
|
// Eventually, we want to try and unravel this as much as we can, to
|
|
// the point that ultimately all C flags and even link options are
|
|
// self-contained in the translation artifact.
|
|
//
|
|
// This is a bit tricky right now as there are situations where we
|
|
// provide a static library built straight off of the C file, hence the
|
|
// duplication.
|
|
//
|
|
// NOTE: This function de-allocates nothing as b.allocator is an arena
|
|
// (unfortunately not documented, but a cursory search in various
|
|
// communities or the issue trackers should turn up confirmation).
|
|
|
|
const translate_c = b.lazyImport(root_build_container, "translate_c") orelse return;
|
|
const translate_c_dep = b.lazyDependency("translate_c", .{}) orelse return;
|
|
|
|
const c_source = c_source: {
|
|
var source_builder: std.Io.Writer.Allocating = .init(b.allocator);
|
|
try fmtInclude(&source_builder.writer, "hb.h", options.harfbuzz);
|
|
|
|
if (options.coretext) {
|
|
try fmtInclude(&source_builder.writer, "hb-coretext.h", options.harfbuzz);
|
|
}
|
|
|
|
if (options.freetype != null) {
|
|
try fmtInclude(&source_builder.writer, "hb-ft.h", options.harfbuzz);
|
|
}
|
|
|
|
break :c_source source_builder.written();
|
|
};
|
|
|
|
// Assemble system libs
|
|
const system_libs = libs: {
|
|
var libs_builder: std.ArrayList(translate_c.Translator.LinkSystemLib) = .empty;
|
|
if (options.harfbuzz == .dynamic)
|
|
try libs_builder.append(b.allocator, .{ .name = "harfbuzz", .options = options.harfbuzz.dynamic });
|
|
if (options.freetype) |ft| {
|
|
if (ft == .dynamic)
|
|
try libs_builder.append(b.allocator, .{ .name = "freetype2", .options = ft.dynamic });
|
|
}
|
|
|
|
break :libs libs_builder.items;
|
|
};
|
|
|
|
// Assemble flags
|
|
const flags = flags: {
|
|
var flag_builder: std.ArrayList([]const u8) = .empty;
|
|
try flag_builder.appendSlice(b.allocator, &.{
|
|
"-DHAVE_STDBOOL_H",
|
|
});
|
|
// 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.
|
|
if (options.target.result.abi == .msvc) {
|
|
try flag_builder.appendSlice(b.allocator, &.{
|
|
"-fno-sanitize=undefined",
|
|
"-fno-sanitize-trap=undefined",
|
|
});
|
|
}
|
|
if (options.target.result.os.tag != .windows) {
|
|
try flag_builder.appendSlice(b.allocator, &.{
|
|
"-DHAVE_UNISTD_H",
|
|
"-DHAVE_SYS_MMAN_H",
|
|
"-DHAVE_PTHREAD=1",
|
|
});
|
|
}
|
|
|
|
// Freetype flags/non-system include paths
|
|
if (options.freetype != null) {
|
|
try flag_builder.appendSlice(b.allocator, &.{
|
|
"-DHAVE_FREETYPE=1",
|
|
|
|
// Let's just assume a new freetype
|
|
"-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1",
|
|
"-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1",
|
|
"-DHAVE_FT_DONE_MM_VAR=1",
|
|
"-DHAVE_FT_GET_TRANSFORM=1",
|
|
});
|
|
}
|
|
|
|
// Coretext
|
|
if (options.coretext) {
|
|
try flag_builder.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"});
|
|
try flag_builder.appendSlice(b.allocator, &.{"-fblocks"});
|
|
}
|
|
|
|
break :flags flag_builder.items;
|
|
};
|
|
|
|
const hb_c: translate_c.Translator = .init(translate_c_dep, .{
|
|
.c_source_file = b.addWriteFiles().add(
|
|
"hb_c.h",
|
|
c_source,
|
|
),
|
|
.target = options.target,
|
|
.optimize = options.optimize,
|
|
.link_libc = true,
|
|
.link_system_libs = system_libs,
|
|
.libc_file = if (options.target.result.os.tag.isDarwin()) libc_file: {
|
|
switch (try @import("apple_sdk").pathsForTarget(b, options.target.result)) {
|
|
inline else => |paths| break :libc_file paths.libc,
|
|
}
|
|
} else null,
|
|
.extra_args = flags,
|
|
});
|
|
|
|
if (options.harfbuzz == .static) {
|
|
if (b.lazyDependency("harfbuzz", .{})) |upstream| {
|
|
hb_c.addIncludePath(upstream.path("src"));
|
|
}
|
|
}
|
|
|
|
// Freetype non-system include paths
|
|
if (options.freetype) |freetype_enabled| {
|
|
if (freetype_enabled == .static) {
|
|
const ft_dep = b.dependency("freetype", .{
|
|
.target = options.target,
|
|
.optimize = options.optimize,
|
|
.@"enable-libpng" = true,
|
|
});
|
|
|
|
if (ft_dep.builder.lazyDependency(
|
|
"freetype",
|
|
.{},
|
|
)) |freetype_lazy_dep| {
|
|
hb_c.addIncludePath(freetype_lazy_dep.path("include"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Coretext
|
|
if (options.coretext) {
|
|
// NOTE: We should not necessarily need to add this directly to C
|
|
// translation, so we just add it to the module.
|
|
hb_c.mod.linkFramework("CoreText", .{});
|
|
}
|
|
|
|
module.addImport("hb_c", hb_c.mod);
|
|
}
|
|
};
|