mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
747 lines
27 KiB
Zig
747 lines
27 KiB
Zig
const std = @import("std");
|
||
const builtin = @import("builtin");
|
||
const fs = std.fs;
|
||
const Builder = std.build.Builder;
|
||
const LibExeObjStep = std.build.LibExeObjStep;
|
||
const apprt = @import("src/apprt.zig");
|
||
const glfw = @import("vendor/mach/libs/glfw/build.zig");
|
||
const fontconfig = @import("pkg/fontconfig/build.zig");
|
||
const freetype = @import("pkg/freetype/build.zig");
|
||
const harfbuzz = @import("pkg/harfbuzz/build.zig");
|
||
const imgui = @import("pkg/imgui/build.zig");
|
||
const js = @import("vendor/zig-js/build.zig");
|
||
const libxev = @import("vendor/libxev/build.zig");
|
||
const libxml2 = @import("vendor/zig-libxml2/libxml2.zig");
|
||
const libpng = @import("pkg/libpng/build.zig");
|
||
const macos = @import("pkg/macos/build.zig");
|
||
const objc = @import("vendor/zig-objc/build.zig");
|
||
const pixman = @import("pkg/pixman/build.zig");
|
||
const stb_image_resize = @import("pkg/stb_image_resize/build.zig");
|
||
const utf8proc = @import("pkg/utf8proc/build.zig");
|
||
const zlib = @import("pkg/zlib/build.zig");
|
||
const tracylib = @import("pkg/tracy/build.zig");
|
||
const system_sdk = @import("vendor/mach/libs/glfw/system_sdk.zig");
|
||
const WasmTarget = @import("src/os/wasm/target.zig").Target;
|
||
const LibtoolStep = @import("src/build/LibtoolStep.zig");
|
||
const LipoStep = @import("src/build/LipoStep.zig");
|
||
const XCFrameworkStep = @import("src/build/XCFrameworkStep.zig");
|
||
|
||
// Do a comptime Zig version requirement. The required Zig version is
|
||
// somewhat arbitrary: it is meant to be a version that we feel works well,
|
||
// but we liberally update it. In the future, we'll be more careful about
|
||
// using released versions so that package managers can integrate better.
|
||
comptime {
|
||
const required_zig = "0.11.0-dev.1635+d09e39aef";
|
||
const current_zig = builtin.zig_version;
|
||
const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable;
|
||
if (current_zig.order(min_zig) == .lt) {
|
||
@compileError(std.fmt.comptimePrint(
|
||
"Your Zig version v{} does not meet the minimum build requirement of v{}",
|
||
.{ current_zig, min_zig },
|
||
));
|
||
}
|
||
}
|
||
|
||
// Build options, see the build options help for more info.
|
||
var tracy: bool = false;
|
||
var enable_coretext: bool = false;
|
||
var enable_fontconfig: bool = false;
|
||
var flatpak: bool = false;
|
||
var app_runtime: apprt.Runtime = .none;
|
||
|
||
pub fn build(b: *std.build.Builder) !void {
|
||
const optimize = b.standardOptimizeOption(.{});
|
||
const target = target: {
|
||
var result = b.standardTargetOptions(.{});
|
||
|
||
if (result.isLinux() and result.isGnuLibC()) {
|
||
// https://github.com/ziglang/zig/issues/9485
|
||
result.glibc_version = .{ .major = 2, .minor = 28 };
|
||
}
|
||
|
||
break :target result;
|
||
};
|
||
|
||
tracy = b.option(
|
||
bool,
|
||
"tracy",
|
||
"Enable Tracy integration (default true in Debug on Linux)",
|
||
) orelse (optimize == .Debug and target.isLinux());
|
||
|
||
flatpak = b.option(
|
||
bool,
|
||
"flatpak",
|
||
"Build for Flatpak (integrates with Flatpak APIs). Only has an effect targeting Linux.",
|
||
) orelse false;
|
||
|
||
enable_coretext = b.option(
|
||
bool,
|
||
"coretext",
|
||
"Enable coretext for font discovery (default true on macOS)",
|
||
) orelse target.isDarwin();
|
||
|
||
enable_fontconfig = b.option(
|
||
bool,
|
||
"fontconfig",
|
||
"Enable fontconfig for font discovery (default true on Linux)",
|
||
) orelse target.isLinux();
|
||
|
||
app_runtime = b.option(
|
||
apprt.Runtime,
|
||
"app-runtime",
|
||
"The app runtime to use. Not all values supported on all platforms.",
|
||
) orelse apprt.Runtime.default(target);
|
||
|
||
const static = b.option(
|
||
bool,
|
||
"static",
|
||
"Statically build as much as possible for the exe",
|
||
) orelse true;
|
||
|
||
const conformance = b.option(
|
||
[]const u8,
|
||
"conformance",
|
||
"Name of the conformance app to run with 'run' option.",
|
||
);
|
||
|
||
const emit_test_exe = b.option(
|
||
bool,
|
||
"emit-test-exe",
|
||
"Build and install test executables with 'build'",
|
||
) orelse false;
|
||
|
||
const emit_bench = b.option(
|
||
bool,
|
||
"emit-bench",
|
||
"Build and install the benchmark executables.",
|
||
) orelse false;
|
||
|
||
// We can use wasmtime to test wasm
|
||
b.enable_wasmtime = true;
|
||
|
||
// Add our benchmarks
|
||
try benchSteps(b, target, optimize, emit_bench);
|
||
|
||
const exe = b.addExecutable(.{
|
||
.name = "ghostty",
|
||
.root_source_file = .{ .path = "src/main.zig" },
|
||
.target = target,
|
||
.optimize = optimize,
|
||
});
|
||
const exe_options = b.addOptions();
|
||
exe_options.addOption(bool, "tracy_enabled", tracy);
|
||
exe_options.addOption(bool, "flatpak", flatpak);
|
||
exe_options.addOption(bool, "coretext", enable_coretext);
|
||
exe_options.addOption(bool, "fontconfig", enable_fontconfig);
|
||
exe_options.addOption(apprt.Runtime, "app_runtime", app_runtime);
|
||
|
||
// Exe
|
||
{
|
||
if (target.isDarwin()) {
|
||
// See the comment in this file
|
||
exe.addCSourceFile("src/renderer/metal_workaround.c", &.{});
|
||
}
|
||
|
||
exe.addOptions("build_options", exe_options);
|
||
if (app_runtime != .none) exe.install();
|
||
|
||
// Add the shared dependencies
|
||
_ = try addDeps(b, exe, static);
|
||
}
|
||
|
||
// App (Linux)
|
||
if (target.isLinux()) {
|
||
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||
|
||
// Desktop file so that we have an icon and other metadata
|
||
if (flatpak) {
|
||
b.installFile("dist/linux/app-flatpak.desktop", "share/applications/com.mitchellh.ghostty.desktop");
|
||
} else {
|
||
b.installFile("dist/linux/app.desktop", "share/applications/com.mitchellh.ghostty.desktop");
|
||
}
|
||
|
||
// Various icons that our application can use, including the icon
|
||
// that will be used for the desktop.
|
||
b.installFile("images/icons/icon_16x16.png", "share/icons/hicolor/16x16/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_32x32.png", "share/icons/hicolor/32x32/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_128x128.png", "share/icons/hicolor/128x128/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_256x256.png", "share/icons/hicolor/256x256/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_512x512.png", "share/icons/hicolor/512x512/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_16x16@2x@2x.png", "share/icons/hicolor/16x16@2/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_32x32@2x@2x.png", "share/icons/hicolor/32x32@2/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_128x128@2x@2x.png", "share/icons/hicolor/128x128@2/com.mitchellh.ghostty.png");
|
||
b.installFile("images/icons/icon_256x256@2x@2x.png", "share/icons/hicolor/256x256@2/com.mitchellh.ghostty.png");
|
||
}
|
||
|
||
// App (Mac)
|
||
if (target.isDarwin()) {
|
||
const bin_path = try std.fmt.allocPrint(b.allocator, "{s}/bin/ghostty", .{b.install_path});
|
||
b.installFile(bin_path, "Ghostty.app/Contents/MacOS/ghostty");
|
||
b.installFile("dist/macos/Info.plist", "Ghostty.app/Contents/Info.plist");
|
||
b.installFile("dist/macos/Ghostty.icns", "Ghostty.app/Contents/Resources/Ghostty.icns");
|
||
}
|
||
|
||
// On Mac we can build the embedding library.
|
||
if (builtin.target.isDarwin()) {
|
||
const static_lib_aarch64 = lib: {
|
||
const lib = b.addStaticLibrary(.{
|
||
.name = "ghostty",
|
||
.root_source_file = .{ .path = "src/main_c.zig" },
|
||
.target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = "aarch64-macos" }),
|
||
.optimize = optimize,
|
||
});
|
||
lib.bundle_compiler_rt = true;
|
||
lib.linkLibC();
|
||
lib.addOptions("build_options", exe_options);
|
||
|
||
// See the comment in this file
|
||
lib.addCSourceFile("src/renderer/metal_workaround.c", &.{});
|
||
|
||
// Create a single static lib with all our dependencies merged
|
||
var lib_list = try addDeps(b, lib, true);
|
||
try lib_list.append(.{ .generated = &lib.output_path_source });
|
||
const libtool = LibtoolStep.create(b, .{
|
||
.name = "ghostty",
|
||
.out_name = "libghostty-aarch64-fat.a",
|
||
.sources = lib_list.items,
|
||
});
|
||
libtool.step.dependOn(&lib.step);
|
||
b.default_step.dependOn(&libtool.step);
|
||
|
||
break :lib libtool;
|
||
};
|
||
|
||
const static_lib_x86_64 = lib: {
|
||
const lib = b.addStaticLibrary(.{
|
||
.name = "ghostty",
|
||
.root_source_file = .{ .path = "src/main_c.zig" },
|
||
.target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = "x86_64-macos" }),
|
||
.optimize = optimize,
|
||
});
|
||
lib.bundle_compiler_rt = true;
|
||
lib.linkLibC();
|
||
lib.addOptions("build_options", exe_options);
|
||
|
||
// See the comment in this file
|
||
lib.addCSourceFile("src/renderer/metal_workaround.c", &.{});
|
||
|
||
// Create a single static lib with all our dependencies merged
|
||
var lib_list = try addDeps(b, lib, true);
|
||
try lib_list.append(.{ .generated = &lib.output_path_source });
|
||
const libtool = LibtoolStep.create(b, .{
|
||
.name = "ghostty",
|
||
.out_name = "libghostty-x86_64-fat.a",
|
||
.sources = lib_list.items,
|
||
});
|
||
libtool.step.dependOn(&lib.step);
|
||
b.default_step.dependOn(&libtool.step);
|
||
|
||
break :lib libtool;
|
||
};
|
||
|
||
const static_lib_universal = LipoStep.create(b, .{
|
||
.name = "ghostty",
|
||
.out_name = "libghostty.a",
|
||
.input_a = .{ .generated = &static_lib_aarch64.out_path },
|
||
.input_b = .{ .generated = &static_lib_x86_64.out_path },
|
||
});
|
||
static_lib_universal.step.dependOn(&static_lib_aarch64.step);
|
||
static_lib_universal.step.dependOn(&static_lib_x86_64.step);
|
||
|
||
// The xcframework wraps our ghostty library so that we can link
|
||
// it to the final app built with Swift.
|
||
const xcframework = XCFrameworkStep.create(b, .{
|
||
.name = "GhosttyKit",
|
||
.out_path = "macos/GhosttyKit.xcframework",
|
||
.library = .{ .generated = &static_lib_universal.out_path },
|
||
.headers = .{ .path = "include" },
|
||
});
|
||
xcframework.step.dependOn(&static_lib_universal.step);
|
||
b.default_step.dependOn(&xcframework.step);
|
||
}
|
||
|
||
// wasm
|
||
{
|
||
// Build our Wasm target.
|
||
const wasm_target: std.zig.CrossTarget = .{
|
||
.cpu_arch = .wasm32,
|
||
.os_tag = .freestanding,
|
||
.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
|
||
.cpu_features_add = std.Target.wasm.featureSet(&.{
|
||
// We use this to explicitly request shared memory.
|
||
.atomics,
|
||
|
||
// Not explicitly used but compiler could use them if they want.
|
||
.bulk_memory,
|
||
.reference_types,
|
||
.sign_ext,
|
||
}),
|
||
};
|
||
|
||
// Whether we're using wasm shared memory. Some behaviors change.
|
||
// For now we require this but I wanted to make the code handle both
|
||
// up front.
|
||
const wasm_shared: bool = true;
|
||
exe_options.addOption(bool, "wasm_shared", wasm_shared);
|
||
|
||
// We want to support alternate wasm targets in the future (i.e.
|
||
// server side) so we have this now although its hardcoded.
|
||
const wasm_specific_target: WasmTarget = .browser;
|
||
exe_options.addOption(WasmTarget, "wasm_target", wasm_specific_target);
|
||
|
||
const wasm = b.addSharedLibrary(.{
|
||
.name = "ghostty-wasm",
|
||
.root_source_file = .{ .path = "src/main_wasm.zig" },
|
||
.target = wasm_target,
|
||
.optimize = optimize,
|
||
});
|
||
wasm.setOutputDir("zig-out");
|
||
wasm.addOptions("build_options", exe_options);
|
||
|
||
// So that we can use web workers with our wasm binary
|
||
wasm.import_memory = true;
|
||
wasm.initial_memory = 65536 * 25;
|
||
wasm.max_memory = 65536 * 65536; // Maximum number of pages in wasm32
|
||
wasm.shared_memory = wasm_shared;
|
||
|
||
// Stack protector adds extern requirements that we don't satisfy.
|
||
wasm.stack_protector = false;
|
||
|
||
// Wasm-specific deps
|
||
_ = try addDeps(b, wasm, true);
|
||
|
||
const step = b.step("wasm", "Build the wasm library");
|
||
step.dependOn(&wasm.step);
|
||
|
||
// We support tests via wasmtime. wasmtime uses WASI so this
|
||
// isn't an exact match to our freestanding target above but
|
||
// it lets us test some basic functionality.
|
||
const test_step = b.step("test-wasm", "Run all tests for wasm");
|
||
const main_test = b.addTest(.{
|
||
.name = "wasm-test",
|
||
.root_source_file = .{ .path = "src/main_wasm.zig" },
|
||
.target = wasm_target,
|
||
});
|
||
main_test.addOptions("build_options", exe_options);
|
||
_ = try addDeps(b, main_test, true);
|
||
test_step.dependOn(&main_test.step);
|
||
}
|
||
|
||
// Run
|
||
{
|
||
// Build our run step, which runs the main app by default, but will
|
||
// run a conformance app if `-Dconformance` is set.
|
||
const run_exe = if (conformance) |name| blk: {
|
||
var conformance_exes = try conformanceSteps(b, target, optimize);
|
||
defer conformance_exes.deinit();
|
||
break :blk conformance_exes.get(name) orelse return error.InvalidConformance;
|
||
} else exe;
|
||
const run_cmd = run_exe.run();
|
||
run_cmd.step.dependOn(&run_exe.step);
|
||
if (b.args) |args| {
|
||
run_cmd.addArgs(args);
|
||
}
|
||
|
||
const run_step = b.step("run", "Run the app");
|
||
run_step.dependOn(&run_cmd.step);
|
||
}
|
||
|
||
// Tests
|
||
{
|
||
const test_step = b.step("test", "Run all tests");
|
||
var test_filter = b.option([]const u8, "test-filter", "Filter for test");
|
||
|
||
const main_test = b.addTest(.{
|
||
.name = "ghostty-test",
|
||
.root_source_file = .{ .path = "src/main.zig" },
|
||
.target = target,
|
||
});
|
||
{
|
||
if (emit_test_exe) {
|
||
const main_test_exe = b.addTest(.{
|
||
.name = "ghostty-test",
|
||
.kind = .test_exe,
|
||
.root_source_file = .{ .path = "src/main.zig" },
|
||
.target = target,
|
||
});
|
||
main_test_exe.install();
|
||
}
|
||
main_test.setFilter(test_filter);
|
||
_ = try addDeps(b, main_test, true);
|
||
main_test.addOptions("build_options", exe_options);
|
||
|
||
var before = b.addLog("\x1b[" ++ color_map.get("cyan").? ++ "\x1b[" ++ color_map.get("b").? ++ "[{s} tests]" ++ "\x1b[" ++ color_map.get("d").? ++ " ----" ++ "\x1b[0m", .{"ghostty"});
|
||
var after = b.addLog("\x1b[" ++ color_map.get("d").? ++ "–––---\n\n" ++ "\x1b[0m", .{});
|
||
test_step.dependOn(&before.step);
|
||
test_step.dependOn(&main_test.step);
|
||
test_step.dependOn(&after.step);
|
||
}
|
||
|
||
// Named package dependencies don't have their tests run by reference,
|
||
// so we iterate through them here. We're only interested in dependencies
|
||
// we wrote (are in the "pkg/" directory).
|
||
var it = main_test.modules.iterator();
|
||
while (it.next()) |entry| {
|
||
const name = entry.key_ptr.*;
|
||
const module = entry.value_ptr.*;
|
||
if (std.mem.eql(u8, name, "build_options")) continue;
|
||
if (std.mem.eql(u8, name, "glfw")) continue;
|
||
|
||
var buf: [256]u8 = undefined;
|
||
var test_run = b.addTest(.{
|
||
.name = try std.fmt.bufPrint(&buf, "{s}-test", .{name}),
|
||
.kind = .@"test",
|
||
.root_source_file = module.source_file,
|
||
.target = target,
|
||
});
|
||
|
||
_ = try addDeps(b, test_run, true);
|
||
// if (pkg.dependencies) |children| {
|
||
// test_.packages = std.ArrayList(std.build.Pkg).init(b.allocator);
|
||
// try test_.packages.appendSlice(children);
|
||
// }
|
||
|
||
var before = b.addLog("\x1b[" ++ color_map.get("cyan").? ++ "\x1b[" ++ color_map.get("b").? ++ "[{s} tests]" ++ "\x1b[" ++ color_map.get("d").? ++ " ----" ++ "\x1b[0m", .{name});
|
||
var after = b.addLog("\x1b[" ++ color_map.get("d").? ++ "–––---\n\n" ++ "\x1b[0m", .{});
|
||
test_step.dependOn(&before.step);
|
||
test_step.dependOn(&test_run.step);
|
||
test_step.dependOn(&after.step);
|
||
|
||
if (emit_test_exe) {
|
||
const test_exe = b.addTest(.{
|
||
.name = try std.fmt.bufPrint(&buf, "{s}-test", .{name}),
|
||
.kind = .test_exe,
|
||
.root_source_file = module.source_file,
|
||
.target = target,
|
||
});
|
||
test_exe.install();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Used to keep track of a list of file sources.
|
||
const FileSourceList = std.ArrayList(std.build.FileSource);
|
||
|
||
/// Adds and links all of the primary dependencies for the exe.
|
||
fn addDeps(
|
||
b: *std.build.Builder,
|
||
step: *std.build.LibExeObjStep,
|
||
static: bool,
|
||
) !FileSourceList {
|
||
var static_libs = FileSourceList.init(b.allocator);
|
||
errdefer static_libs.deinit();
|
||
|
||
// Wasm we do manually since it is such a different build.
|
||
if (step.target.getCpuArch() == .wasm32) {
|
||
// We link this package but its a no-op since Tracy
|
||
// never actualy WORKS with wasm.
|
||
step.addModule("tracy", tracylib.module(b));
|
||
step.addModule("utf8proc", utf8proc.module(b));
|
||
step.addModule("zig-js", js.module(b));
|
||
|
||
// utf8proc
|
||
_ = try utf8proc.link(b, step);
|
||
|
||
return static_libs;
|
||
}
|
||
|
||
// If we're building a lib we have some different deps
|
||
const lib = step.kind == .lib;
|
||
|
||
// We always require the system SDK so that our system headers are available.
|
||
// This makes things like `os/log.h` available for cross-compiling.
|
||
system_sdk.include(b, step, .{});
|
||
|
||
// We always need the Zig packages
|
||
// TODO: This can't be the right way to use the new Zig modules system,
|
||
// so take a closer look at this again later.
|
||
if (enable_fontconfig) step.addModule("fontconfig", fontconfig.module(b));
|
||
const mod_freetype = freetype.module(b);
|
||
const mod_macos = macos.module(b);
|
||
step.addModule("freetype", mod_freetype);
|
||
step.addModule("harfbuzz", harfbuzz.module(b, .{
|
||
.freetype = mod_freetype,
|
||
.macos = mod_macos,
|
||
}));
|
||
step.addModule("imgui", imgui.module(b));
|
||
step.addModule("xev", libxev.module(b));
|
||
step.addModule("pixman", pixman.module(b));
|
||
step.addModule("stb_image_resize", stb_image_resize.module(b));
|
||
step.addModule("utf8proc", utf8proc.module(b));
|
||
|
||
// Mac Stuff
|
||
if (step.target.isDarwin()) {
|
||
step.addModule("objc", objc.module(b));
|
||
step.addModule("macos", mod_macos);
|
||
_ = try macos.link(b, step, .{});
|
||
}
|
||
|
||
// Tracy
|
||
step.addModule("tracy", tracylib.module(b));
|
||
if (tracy) {
|
||
var tracy_step = try tracylib.link(b, step);
|
||
system_sdk.include(b, tracy_step, .{});
|
||
}
|
||
|
||
// stb_image_resize
|
||
const stb_image_resize_step = try stb_image_resize.link(b, step, .{});
|
||
try static_libs.append(.{ .generated = &stb_image_resize_step.output_path_source });
|
||
|
||
// utf8proc
|
||
const utf8proc_step = try utf8proc.link(b, step);
|
||
try static_libs.append(.{ .generated = &utf8proc_step.output_path_source });
|
||
|
||
// Imgui, we have to do this later since we need some information
|
||
const imgui_backends = if (step.target.isDarwin())
|
||
&[_][]const u8{ "glfw", "opengl3", "metal" }
|
||
else
|
||
&[_][]const u8{ "glfw", "opengl3" };
|
||
var imgui_opts: imgui.Options = .{
|
||
.backends = imgui_backends,
|
||
.freetype = .{ .enabled = true },
|
||
};
|
||
|
||
// Dynamic link
|
||
if (!static) {
|
||
step.addIncludePath(freetype.include_path_self);
|
||
step.linkSystemLibrary("bzip2");
|
||
step.linkSystemLibrary("freetype2");
|
||
step.linkSystemLibrary("harfbuzz");
|
||
step.linkSystemLibrary("libpng");
|
||
step.linkSystemLibrary("pixman-1");
|
||
step.linkSystemLibrary("zlib");
|
||
|
||
if (enable_fontconfig) step.linkSystemLibrary("fontconfig");
|
||
}
|
||
|
||
// Other dependencies, we may dynamically link
|
||
if (static) {
|
||
const zlib_step = try zlib.link(b, step);
|
||
try static_libs.append(.{ .generated = &zlib_step.output_path_source });
|
||
|
||
const libpng_step = try libpng.link(b, step, .{
|
||
.zlib = .{
|
||
.step = zlib_step,
|
||
.include = &zlib.include_paths,
|
||
},
|
||
});
|
||
try static_libs.append(.{ .generated = &libpng_step.output_path_source });
|
||
|
||
// Freetype
|
||
const freetype_step = try freetype.link(b, step, .{
|
||
.libpng = freetype.Options.Libpng{
|
||
.enabled = true,
|
||
.step = libpng_step,
|
||
.include = &libpng.include_paths,
|
||
},
|
||
|
||
.zlib = .{
|
||
.enabled = true,
|
||
.step = zlib_step,
|
||
.include = &zlib.include_paths,
|
||
},
|
||
});
|
||
try static_libs.append(.{ .generated = &freetype_step.output_path_source });
|
||
|
||
// Harfbuzz
|
||
const harfbuzz_step = try harfbuzz.link(b, step, .{
|
||
.freetype = .{
|
||
.enabled = true,
|
||
.step = freetype_step,
|
||
.include = &freetype.include_paths,
|
||
},
|
||
|
||
.coretext = .{
|
||
.enabled = enable_coretext,
|
||
},
|
||
});
|
||
system_sdk.include(b, harfbuzz_step, .{});
|
||
try static_libs.append(.{ .generated = &harfbuzz_step.output_path_source });
|
||
|
||
// Pixman
|
||
const pixman_step = try pixman.link(b, step, .{});
|
||
try static_libs.append(.{ .generated = &pixman_step.output_path_source });
|
||
|
||
// Only Linux gets fontconfig
|
||
if (enable_fontconfig) {
|
||
// Libxml2
|
||
const libxml2_lib = try libxml2.create(
|
||
b,
|
||
step.target,
|
||
step.optimize,
|
||
.{ .lzma = false, .zlib = false },
|
||
);
|
||
libxml2_lib.link(step);
|
||
|
||
// Fontconfig
|
||
const fontconfig_step = try fontconfig.link(b, step, .{
|
||
.freetype = .{
|
||
.enabled = true,
|
||
.step = freetype_step,
|
||
.include = &freetype.include_paths,
|
||
},
|
||
|
||
.libxml2 = true,
|
||
});
|
||
libxml2_lib.link(fontconfig_step);
|
||
}
|
||
|
||
// Imgui
|
||
imgui_opts.freetype.step = freetype_step;
|
||
imgui_opts.freetype.include = &freetype.include_paths;
|
||
}
|
||
|
||
if (!lib) {
|
||
// We always statically compile glad
|
||
step.addIncludePath("vendor/glad/include/");
|
||
step.addCSourceFile("vendor/glad/src/gl.c", &.{});
|
||
|
||
// When we're targeting flatpak we ALWAYS link GTK so we
|
||
// get access to glib for dbus.
|
||
if (flatpak) {
|
||
step.linkSystemLibrary("gtk4");
|
||
switch (step.target.getCpuArch()) {
|
||
.aarch64 => step.addLibraryPath("/usr/lib/aarch64-linux-gnu"),
|
||
.x86_64 => step.addLibraryPath("/usr/lib/x86_64-linux-gnu"),
|
||
else => @panic("unsupported flatpak target"),
|
||
}
|
||
}
|
||
|
||
switch (app_runtime) {
|
||
.none => {},
|
||
|
||
.glfw => {
|
||
step.addModule("glfw", glfw.module(b));
|
||
const glfw_opts: glfw.Options = .{
|
||
.metal = step.target.isDarwin(),
|
||
.opengl = false,
|
||
};
|
||
try glfw.link(b, step, glfw_opts);
|
||
|
||
// Must also link to imgui
|
||
const imgui_step = try imgui.link(b, step, imgui_opts);
|
||
try glfw.link(b, imgui_step, glfw_opts);
|
||
},
|
||
|
||
.gtk => {
|
||
// We need glfw for GTK because we use GLFW to get DPI.
|
||
step.addModule("glfw", glfw.module(b));
|
||
const glfw_opts: glfw.Options = .{
|
||
.metal = step.target.isDarwin(),
|
||
.opengl = false,
|
||
};
|
||
try glfw.link(b, step, glfw_opts);
|
||
|
||
step.linkSystemLibrary("gtk4");
|
||
},
|
||
}
|
||
}
|
||
|
||
return static_libs;
|
||
}
|
||
|
||
fn benchSteps(
|
||
b: *std.build.Builder,
|
||
target: std.zig.CrossTarget,
|
||
optimize: std.builtin.Mode,
|
||
install: bool,
|
||
) !void {
|
||
// Open the directory ./src/bench
|
||
const c_dir_path = (comptime root()) ++ "/src/bench";
|
||
var c_dir = try fs.openIterableDirAbsolute(c_dir_path, .{});
|
||
defer c_dir.close();
|
||
|
||
// Go through and add each as a step
|
||
var c_dir_it = c_dir.iterate();
|
||
while (try c_dir_it.next()) |entry| {
|
||
// Get the index of the last '.' so we can strip the extension.
|
||
const index = std.mem.lastIndexOfScalar(u8, entry.name, '.') orelse continue;
|
||
if (index == 0) continue;
|
||
|
||
// If it doesn't end in 'zig' then ignore
|
||
if (!std.mem.eql(u8, entry.name[index + 1 ..], "zig")) continue;
|
||
|
||
// Name of the conformance app and full path to the entrypoint.
|
||
const name = entry.name[0..index];
|
||
const path = try fs.path.join(b.allocator, &[_][]const u8{
|
||
c_dir_path,
|
||
entry.name,
|
||
});
|
||
|
||
// Executable builder.
|
||
const bin_name = try std.fmt.allocPrint(b.allocator, "bench-{s}", .{name});
|
||
const c_exe = b.addExecutable(.{
|
||
.name = bin_name,
|
||
.root_source_file = .{ .path = path },
|
||
.target = target,
|
||
.optimize = optimize,
|
||
});
|
||
c_exe.setMainPkgPath("./src");
|
||
if (install) c_exe.install();
|
||
_ = try addDeps(b, c_exe, true);
|
||
}
|
||
}
|
||
|
||
fn conformanceSteps(
|
||
b: *std.build.Builder,
|
||
target: std.zig.CrossTarget,
|
||
optimize: std.builtin.Mode,
|
||
) !std.StringHashMap(*LibExeObjStep) {
|
||
var map = std.StringHashMap(*LibExeObjStep).init(b.allocator);
|
||
|
||
// Open the directory ./conformance
|
||
const c_dir_path = (comptime root()) ++ "/conformance";
|
||
var c_dir = try fs.openIterableDirAbsolute(c_dir_path, .{});
|
||
defer c_dir.close();
|
||
|
||
// Go through and add each as a step
|
||
var c_dir_it = c_dir.iterate();
|
||
while (try c_dir_it.next()) |entry| {
|
||
// Get the index of the last '.' so we can strip the extension.
|
||
const index = std.mem.lastIndexOfScalar(u8, entry.name, '.') orelse continue;
|
||
if (index == 0) continue;
|
||
|
||
// Name of the conformance app and full path to the entrypoint.
|
||
const name = entry.name[0..index];
|
||
const path = try fs.path.join(b.allocator, &[_][]const u8{
|
||
c_dir_path,
|
||
entry.name,
|
||
});
|
||
|
||
// Executable builder.
|
||
const c_exe = b.addExecutable(.{
|
||
.name = name,
|
||
.root_source_file = .{ .path = path },
|
||
.target = target,
|
||
.optimize = optimize,
|
||
});
|
||
c_exe.setOutputDir("zig-out/bin/conformance");
|
||
c_exe.install();
|
||
|
||
// Store the mapping
|
||
try map.put(name, c_exe);
|
||
}
|
||
|
||
return map;
|
||
}
|
||
|
||
/// Path to the directory with the build.zig.
|
||
fn root() []const u8 {
|
||
return std.fs.path.dirname(@src().file) orelse unreachable;
|
||
}
|
||
|
||
/// ANSI escape codes for colored log output
|
||
const color_map = std.ComptimeStringMap([]const u8, .{
|
||
&.{ "black", "30m" },
|
||
&.{ "blue", "34m" },
|
||
&.{ "b", "1m" },
|
||
&.{ "d", "2m" },
|
||
&.{ "cyan", "36m" },
|
||
&.{ "green", "32m" },
|
||
&.{ "magenta", "35m" },
|
||
&.{ "red", "31m" },
|
||
&.{ "white", "37m" },
|
||
&.{ "yellow", "33m" },
|
||
});
|