mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
build.zig Improvements (#4797)
This is a major refactor of `build.zig` aimed at improving maintainability while also fixing some bugs. The major idea behind the refactor is to split the `build.zig` file up into distinct `src/build/*.zig` files. By doing so, we can improve readability of the primary `build.zig` while also enabling better reuse of steps. Our `build.zig` is now less than 150 lines of code (of course, it calls into a lot more lines but they're neatly organized now). Ignore the commit messages I'm going to squash this whole thing. Improvements: * `build.zig` is less than 150 lines of readable code. * Help strings and unicode table generators are only run once when multiple artifacts are built since the results are the same regardless of target. * Metal lib is only built once per architecture (rather than once per artifact) * Resources (shell integration, terminfo, etc.) and docs are only built/installed for artifacts that need them * Wayland protocols no longer required for source build Breaking changes: * Removed broken wasm build (@gabydd will re-add) * Removed conformance files, shell scripts are better and we don't run these anymore * Removed macOS app bundle creation, we don't use this anymore since we use Xcode ## Extra: Some History Our `build.zig` hasn't been significantly refactored since the project started, when Zig was _version 0.10_. Since then, the build system has changed significantly. We've only ever duct taped the `build.zig` as we needed to support new Zig versions, new features, etc. It was a mess. The major improvement is adapting the entire Ghostty `build.zig` to the Step and LazyPath changes introduced way back in Zig 0.12. This lets us better take advantage of parallelism and the dependency graph so that steps are only executed as they're needed. As such, you can see in the build.zig that we initialize a lot of things, but unless a final target (i.e. install, run) references those steps, _they'll never be executed_. This lets us clean up a lot.
This commit is contained in:
@ -13,6 +13,14 @@
|
|||||||
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
|
.vaxis = .{
|
||||||
|
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
|
||||||
|
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
||||||
|
},
|
||||||
|
.z2d = .{
|
||||||
|
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
|
||||||
|
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
||||||
|
},
|
||||||
.zig_objc = .{
|
.zig_objc = .{
|
||||||
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
||||||
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
||||||
@ -29,6 +37,10 @@
|
|||||||
.url = "https://codeberg.org/ifreund/zig-wayland/archive/a5e2e9b6a6d7fba638ace4d4b24a3b576a02685b.tar.gz",
|
.url = "https://codeberg.org/ifreund/zig-wayland/archive/a5e2e9b6a6d7fba638ace4d4b24a3b576a02685b.tar.gz",
|
||||||
.hash = "1220d41b23ae70e93355bb29dac1c07aa6aeb92427a2dffc4375e94b4de18111248c",
|
.hash = "1220d41b23ae70e93355bb29dac1c07aa6aeb92427a2dffc4375e94b4de18111248c",
|
||||||
},
|
},
|
||||||
|
.zf = .{
|
||||||
|
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
|
||||||
|
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
||||||
|
},
|
||||||
|
|
||||||
// C libs
|
// C libs
|
||||||
.cimgui = .{ .path = "./pkg/cimgui" },
|
.cimgui = .{ .path = "./pkg/cimgui" },
|
||||||
@ -50,27 +62,25 @@
|
|||||||
.glslang = .{ .path = "./pkg/glslang" },
|
.glslang = .{ .path = "./pkg/glslang" },
|
||||||
.spirv_cross = .{ .path = "./pkg/spirv-cross" },
|
.spirv_cross = .{ .path = "./pkg/spirv-cross" },
|
||||||
|
|
||||||
|
// Wayland
|
||||||
|
.wayland = .{
|
||||||
|
.url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
|
||||||
|
.hash = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f",
|
||||||
|
},
|
||||||
|
.wayland_protocols = .{
|
||||||
|
.url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
|
||||||
|
.hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef",
|
||||||
|
},
|
||||||
|
.plasma_wayland_protocols = .{
|
||||||
|
.url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86",
|
||||||
|
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
||||||
|
},
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||||
.iterm2_themes = .{
|
.iterm2_themes = .{
|
||||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4762ad5bd6d3906e28babdc2bda8a967d63a63be.tar.gz",
|
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4762ad5bd6d3906e28babdc2bda8a967d63a63be.tar.gz",
|
||||||
.hash = "1220a263b22113273d01bd33e3c06b8119cb2f63b4e5d414a85d88e3aa95bb68a2de",
|
.hash = "1220a263b22113273d01bd33e3c06b8119cb2f63b4e5d414a85d88e3aa95bb68a2de",
|
||||||
},
|
},
|
||||||
.vaxis = .{
|
|
||||||
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
|
|
||||||
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
|
||||||
},
|
|
||||||
.zf = .{
|
|
||||||
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
|
|
||||||
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
|
||||||
},
|
|
||||||
.z2d = .{
|
|
||||||
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
|
|
||||||
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
|
||||||
},
|
|
||||||
.plasma_wayland_protocols = .{
|
|
||||||
.url = "git+https://invent.kde.org/libraries/plasma-wayland-protocols.git?ref=master#db525e8f9da548cffa2ac77618dd0fbe7f511b86",
|
|
||||||
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
//! Reverse Index (RI) - ESC M
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\nC", .{});
|
|
||||||
try stdout.print("\x1BM", .{});
|
|
||||||
try stdout.print("D\n\n", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
//! Reverse Index (RI) - ESC M
|
|
||||||
//! Case: test that if the cursor is at the top, it scrolls down.
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\n\n", .{});
|
|
||||||
|
|
||||||
try stdout.print("\x1B[H", .{}); // Top-left
|
|
||||||
try stdout.print("\x1BM", .{}); // Reverse-Index
|
|
||||||
try stdout.print("D", .{});
|
|
||||||
|
|
||||||
try stdout.print("\x0D", .{}); // CR
|
|
||||||
try stdout.print("\x0A", .{}); // LF
|
|
||||||
try stdout.print("\x1B[H", .{}); // Top-left
|
|
||||||
try stdout.print("\x1BM", .{}); // Reverse-Index
|
|
||||||
try stdout.print("E", .{});
|
|
||||||
|
|
||||||
try stdout.print("\n", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
//! Outputs various box glyphs for testing.
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
|
|
||||||
// Box Drawing
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mBox Drawing\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x2500;
|
|
||||||
const step: usize = 32;
|
|
||||||
while (i <= 0x257F) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block Elements
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mBlock Elements\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x2580;
|
|
||||||
const step: usize = 32;
|
|
||||||
while (i <= 0x259f) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Braille Elements
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mBraille\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x2800;
|
|
||||||
const step: usize = 32;
|
|
||||||
while (i <= 0x28FF) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mSextants\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x1FB00;
|
|
||||||
const step: usize = 32;
|
|
||||||
const end = 0x1FB3B;
|
|
||||||
while (i <= end) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
const v = i + j;
|
|
||||||
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mWedge Triangles\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x1FB3C;
|
|
||||||
const step: usize = 32;
|
|
||||||
const end = 0x1FB6B;
|
|
||||||
while (i <= end) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
const v = i + j;
|
|
||||||
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mOther\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x1FB70;
|
|
||||||
const step: usize = 32;
|
|
||||||
const end = 0x1FB8B;
|
|
||||||
while (i <= end) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
const v = i + j;
|
|
||||||
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
//! Set Top and Bottom Margins (DECSTBM) - ESC [ r
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\nC\nD", .{});
|
|
||||||
try stdout.print("\x1B[1;3r", .{}); // cursor up
|
|
||||||
try stdout.print("\x1B[1;1H", .{}); // top-left
|
|
||||||
try stdout.print("\x1B[M", .{}); // delete line
|
|
||||||
try stdout.print("E\n", .{});
|
|
||||||
try stdout.print("\x1B[7;1H", .{}); // cursor up
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
//! Delete Line (DL) - Esc [ M
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\nC\nD", .{});
|
|
||||||
try stdout.print("\x1B[2A", .{}); // cursor up
|
|
||||||
try stdout.print("\x1B[M", .{});
|
|
||||||
try stdout.print("E\n", .{});
|
|
||||||
try stdout.print("\x1B[B", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
//! Insert Line (IL) - Esc [ L
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("\x1B[2J", .{}); // clear screen
|
|
||||||
try stdout.print("\x1B[1;1H", .{}); // set cursor position
|
|
||||||
try stdout.print("A\nB\nC\nD\nE", .{});
|
|
||||||
try stdout.print("\x1B[1;2r", .{}); // set scroll region
|
|
||||||
try stdout.print("\x1B[1;1H", .{}); // set cursor position
|
|
||||||
try stdout.print("\x1B[1L", .{}); // insert lines
|
|
||||||
try stdout.print("X", .{});
|
|
||||||
try stdout.print("\x1B[7;1H", .{}); // set cursor position
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
//! DECALN - ESC # 8
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("\x1B#8", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@ -53,7 +53,6 @@
|
|||||||
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
||||||
lib.fileset.unions [
|
lib.fileset.unions [
|
||||||
../dist/linux
|
../dist/linux
|
||||||
../conformance
|
|
||||||
../images
|
../images
|
||||||
../include
|
../include
|
||||||
../pkg
|
../pkg
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
||||||
# more details.
|
# more details.
|
||||||
"sha256-eUY6MS3//r6pA/w9b+E4+YqmqUbzpUfL3afJJlnMhLY="
|
"sha256-PnfSy793kcVt85q47kWR0xkivXoMOZAAmuUyKO9vqAI="
|
||||||
|
503
src/build/Config.zig
Normal file
503
src/build/Config.zig
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
/// Build configuration. This is the configuration that is populated
|
||||||
|
/// during `zig build` to control the rest of the build process.
|
||||||
|
const Config = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const apprt = @import("../apprt.zig");
|
||||||
|
const font = @import("../font/main.zig");
|
||||||
|
const renderer = @import("../renderer.zig");
|
||||||
|
const Command = @import("../Command.zig");
|
||||||
|
const WasmTarget = @import("../os/wasm/target.zig").Target;
|
||||||
|
|
||||||
|
const gtk = @import("gtk.zig");
|
||||||
|
const GitVersion = @import("GitVersion.zig");
|
||||||
|
|
||||||
|
/// The version of the next release.
|
||||||
|
///
|
||||||
|
/// TODO: When Zig 0.14 is released, derive this from build.zig.zon directly.
|
||||||
|
/// Until then this MUST match build.zig.zon and should always be the
|
||||||
|
/// _next_ version to release.
|
||||||
|
const app_version: std.SemanticVersion = .{ .major = 1, .minor = 0, .patch = 2 };
|
||||||
|
|
||||||
|
/// Standard build configuration options.
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
target: std.Build.ResolvedTarget,
|
||||||
|
wasm_target: WasmTarget,
|
||||||
|
|
||||||
|
/// Comptime interfaces
|
||||||
|
app_runtime: apprt.Runtime = .none,
|
||||||
|
renderer: renderer.Impl = .opengl,
|
||||||
|
font_backend: font.Backend = .freetype,
|
||||||
|
|
||||||
|
/// Feature flags
|
||||||
|
adwaita: bool = false,
|
||||||
|
x11: bool = false,
|
||||||
|
wayland: bool = false,
|
||||||
|
sentry: bool = true,
|
||||||
|
wasm_shared: bool = true,
|
||||||
|
|
||||||
|
/// Ghostty exe properties
|
||||||
|
exe_entrypoint: ExeEntrypoint = .ghostty,
|
||||||
|
version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
|
||||||
|
|
||||||
|
/// Binary properties
|
||||||
|
pie: bool = false,
|
||||||
|
strip: bool = false,
|
||||||
|
patch_rpath: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// Artifacts
|
||||||
|
flatpak: bool = false,
|
||||||
|
emit_test_exe: bool = false,
|
||||||
|
emit_bench: bool = false,
|
||||||
|
emit_helpgen: bool = false,
|
||||||
|
emit_docs: bool = false,
|
||||||
|
emit_webdata: bool = false,
|
||||||
|
emit_xcframework: bool = false,
|
||||||
|
|
||||||
|
/// Environmental properties
|
||||||
|
env: std.process.EnvMap,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build) !Config {
|
||||||
|
// Setup our standard Zig target and optimize options, i.e.
|
||||||
|
// `-Doptimize` and `-Dtarget`.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
const target = target: {
|
||||||
|
var result = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// If we're building for macOS and we're on macOS, we need to
|
||||||
|
// use a generic target to workaround compilation issues.
|
||||||
|
if (result.result.os.tag == .macos and builtin.target.isDarwin()) {
|
||||||
|
result = genericMacOSTarget(b, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have no minimum OS version, we set the default based on
|
||||||
|
// our tag. Not all tags have a minimum so this may be null.
|
||||||
|
if (result.query.os_version_min == null) {
|
||||||
|
result.query.os_version_min = osVersionMin(result.result.os.tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
break :target result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is set to true when we're building a system package. For now
|
||||||
|
// this is trivially detected using the "system_package_mode" bool
|
||||||
|
// but we may want to make this more sophisticated in the future.
|
||||||
|
const system_package: bool = b.graph.system_package_mode;
|
||||||
|
|
||||||
|
// This specifies our target wasm runtime. For now only one semi-usable
|
||||||
|
// one exists so this is hardcoded.
|
||||||
|
const wasm_target: WasmTarget = .browser;
|
||||||
|
|
||||||
|
// Determine whether GTK supports X11 and Wayland. This is always safe
|
||||||
|
// to run even on non-Linux platforms because any failures result in
|
||||||
|
// defaults.
|
||||||
|
const gtk_targets = gtk.targets(b);
|
||||||
|
|
||||||
|
// We use env vars throughout the build so we grab them immediately here.
|
||||||
|
var env = try std.process.getEnvMap(b.allocator);
|
||||||
|
errdefer env.deinit();
|
||||||
|
|
||||||
|
var config: Config = .{
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
.wasm_target = wasm_target,
|
||||||
|
.env = env,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Comptime Interfaces
|
||||||
|
|
||||||
|
config.font_backend = b.option(
|
||||||
|
font.Backend,
|
||||||
|
"font-backend",
|
||||||
|
"The font backend to use for discovery and rasterization.",
|
||||||
|
) orelse font.Backend.default(target.result, wasm_target);
|
||||||
|
|
||||||
|
config.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.result);
|
||||||
|
|
||||||
|
config.renderer = b.option(
|
||||||
|
renderer.Impl,
|
||||||
|
"renderer",
|
||||||
|
"The app runtime to use. Not all values supported on all platforms.",
|
||||||
|
) orelse renderer.Impl.default(target.result, wasm_target);
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Feature Flags
|
||||||
|
|
||||||
|
config.adwaita = b.option(
|
||||||
|
bool,
|
||||||
|
"gtk-adwaita",
|
||||||
|
"Enables the use of Adwaita when using the GTK rendering backend.",
|
||||||
|
) orelse true;
|
||||||
|
|
||||||
|
config.flatpak = b.option(
|
||||||
|
bool,
|
||||||
|
"flatpak",
|
||||||
|
"Build for Flatpak (integrates with Flatpak APIs). Only has an effect targeting Linux.",
|
||||||
|
) orelse false;
|
||||||
|
|
||||||
|
config.sentry = b.option(
|
||||||
|
bool,
|
||||||
|
"sentry",
|
||||||
|
"Build with Sentry crash reporting. Default for macOS is true, false for any other system.",
|
||||||
|
) orelse sentry: {
|
||||||
|
switch (target.result.os.tag) {
|
||||||
|
.macos, .ios => break :sentry true,
|
||||||
|
|
||||||
|
// Note its false for linux because the crash reports on Linux
|
||||||
|
// don't have much useful information.
|
||||||
|
else => break :sentry false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
config.wayland = b.option(
|
||||||
|
bool,
|
||||||
|
"gtk-wayland",
|
||||||
|
"Enables linking against Wayland libraries when using the GTK rendering backend.",
|
||||||
|
) orelse gtk_targets.wayland;
|
||||||
|
|
||||||
|
config.x11 = b.option(
|
||||||
|
bool,
|
||||||
|
"gtk-x11",
|
||||||
|
"Enables linking against X11 libraries when using the GTK rendering backend.",
|
||||||
|
) orelse gtk_targets.x11;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Ghostty Exe Properties
|
||||||
|
|
||||||
|
const version_string = b.option(
|
||||||
|
[]const u8,
|
||||||
|
"version-string",
|
||||||
|
"A specific version string to use for the build. " ++
|
||||||
|
"If not specified, git will be used. This must be a semantic version.",
|
||||||
|
);
|
||||||
|
|
||||||
|
config.version = if (version_string) |v|
|
||||||
|
// If an explicit version is given, we always use it.
|
||||||
|
try std.SemanticVersion.parse(v)
|
||||||
|
else version: {
|
||||||
|
// If no explicit version is given, we try to detect it from git.
|
||||||
|
const vsn = GitVersion.detect(b) catch |err| switch (err) {
|
||||||
|
// If Git isn't available we just make an unknown dev version.
|
||||||
|
error.GitNotFound,
|
||||||
|
error.GitNotRepository,
|
||||||
|
=> break :version .{
|
||||||
|
.major = app_version.major,
|
||||||
|
.minor = app_version.minor,
|
||||||
|
.patch = app_version.patch,
|
||||||
|
.pre = "dev",
|
||||||
|
.build = "0000000",
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
if (vsn.tag) |tag| {
|
||||||
|
// Tip releases behave just like any other pre-release so we skip.
|
||||||
|
if (!std.mem.eql(u8, tag, "tip")) {
|
||||||
|
const expected = b.fmt("v{d}.{d}.{d}", .{
|
||||||
|
app_version.major,
|
||||||
|
app_version.minor,
|
||||||
|
app_version.patch,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!std.mem.eql(u8, tag, expected)) {
|
||||||
|
@panic("tagged releases must be in vX.Y.Z format matching build.zig");
|
||||||
|
}
|
||||||
|
|
||||||
|
break :version .{
|
||||||
|
.major = app_version.major,
|
||||||
|
.minor = app_version.minor,
|
||||||
|
.patch = app_version.patch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break :version .{
|
||||||
|
.major = app_version.major,
|
||||||
|
.minor = app_version.minor,
|
||||||
|
.patch = app_version.patch,
|
||||||
|
.pre = vsn.branch,
|
||||||
|
.build = vsn.short_hash,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Binary Properties
|
||||||
|
|
||||||
|
// On NixOS, the built binary from `zig build` needs to patch the rpath
|
||||||
|
// into the built binary for it to be portable across the NixOS system
|
||||||
|
// it was built for. We default this to true if we can detect we're in
|
||||||
|
// a Nix shell and have LD_LIBRARY_PATH set.
|
||||||
|
config.patch_rpath = b.option(
|
||||||
|
[]const u8,
|
||||||
|
"patch-rpath",
|
||||||
|
"Inject the LD_LIBRARY_PATH as the rpath in the built binary. " ++
|
||||||
|
"This defaults to LD_LIBRARY_PATH if we're in a Nix shell environment on NixOS.",
|
||||||
|
) orelse patch_rpath: {
|
||||||
|
// We only do the patching if we're targeting our own CPU and its Linux.
|
||||||
|
if (!(target.result.os.tag == .linux) or !target.query.isNativeCpu()) break :patch_rpath null;
|
||||||
|
|
||||||
|
// If we're in a nix shell we default to doing this.
|
||||||
|
// Note: we purposely never deinit envmap because we leak the strings
|
||||||
|
if (env.get("IN_NIX_SHELL") == null) break :patch_rpath null;
|
||||||
|
break :patch_rpath env.get("LD_LIBRARY_PATH");
|
||||||
|
};
|
||||||
|
|
||||||
|
config.pie = b.option(
|
||||||
|
bool,
|
||||||
|
"pie",
|
||||||
|
"Build a Position Independent Executable. Default true for system packages.",
|
||||||
|
) orelse system_package;
|
||||||
|
|
||||||
|
config.strip = b.option(
|
||||||
|
bool,
|
||||||
|
"strip",
|
||||||
|
"Strip the final executable. Default true for fast and small releases",
|
||||||
|
) orelse switch (optimize) {
|
||||||
|
.Debug => false,
|
||||||
|
.ReleaseSafe => false,
|
||||||
|
.ReleaseFast, .ReleaseSmall => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Artifacts to Emit
|
||||||
|
|
||||||
|
config.emit_test_exe = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-test-exe",
|
||||||
|
"Build and install test executables with 'build'",
|
||||||
|
) orelse false;
|
||||||
|
|
||||||
|
config.emit_bench = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-bench",
|
||||||
|
"Build and install the benchmark executables.",
|
||||||
|
) orelse false;
|
||||||
|
|
||||||
|
config.emit_helpgen = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-helpgen",
|
||||||
|
"Build and install the helpgen executable.",
|
||||||
|
) orelse false;
|
||||||
|
|
||||||
|
config.emit_docs = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-docs",
|
||||||
|
"Build and install auto-generated documentation (requires pandoc)",
|
||||||
|
) orelse emit_docs: {
|
||||||
|
// If we are emitting any other artifacts then we default to false.
|
||||||
|
if (config.emit_bench or
|
||||||
|
config.emit_test_exe or
|
||||||
|
config.emit_helpgen) break :emit_docs false;
|
||||||
|
|
||||||
|
// We always emit docs in system package mode.
|
||||||
|
if (system_package) break :emit_docs true;
|
||||||
|
|
||||||
|
// We only default to true if we can find pandoc.
|
||||||
|
const path = Command.expandPath(b.allocator, "pandoc") catch
|
||||||
|
break :emit_docs false;
|
||||||
|
defer if (path) |p| b.allocator.free(p);
|
||||||
|
break :emit_docs path != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
config.emit_webdata = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-webdata",
|
||||||
|
"Build the website data for the website.",
|
||||||
|
) orelse false;
|
||||||
|
|
||||||
|
config.emit_xcframework = b.option(
|
||||||
|
bool,
|
||||||
|
"emit-xcframework",
|
||||||
|
"Build and install the xcframework for the macOS library.",
|
||||||
|
) orelse builtin.target.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);
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// System Packages
|
||||||
|
|
||||||
|
// These are all our dependencies that can be used with system
|
||||||
|
// packages if they exist. We set them up here so that we can set
|
||||||
|
// their defaults early. The first call configures the integration and
|
||||||
|
// subsequent calls just return the configured value. This lets them
|
||||||
|
// show up properly in `--help`.
|
||||||
|
|
||||||
|
{
|
||||||
|
// These dependencies we want to default false if we're on macOS.
|
||||||
|
// On macOS we don't want to use system libraries because we
|
||||||
|
// generally want a fat binary. This can be overridden with the
|
||||||
|
// `-fsys` flag.
|
||||||
|
for (&[_][]const u8{
|
||||||
|
"freetype",
|
||||||
|
"harfbuzz",
|
||||||
|
"fontconfig",
|
||||||
|
"libpng",
|
||||||
|
"zlib",
|
||||||
|
"oniguruma",
|
||||||
|
}) |dep| {
|
||||||
|
_ = b.systemIntegrationOption(
|
||||||
|
dep,
|
||||||
|
.{
|
||||||
|
// If we're not on darwin we want to use whatever the
|
||||||
|
// default is via the system package mode
|
||||||
|
.default = if (target.result.isDarwin()) false else null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These default to false because they're rarely available as
|
||||||
|
// system packages so we usually want to statically link them.
|
||||||
|
for (&[_][]const u8{
|
||||||
|
"glslang",
|
||||||
|
"spirv-cross",
|
||||||
|
"simdutf",
|
||||||
|
}) |dep| {
|
||||||
|
_ = b.systemIntegrationOption(dep, .{ .default = false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the build options with our values.
|
||||||
|
pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void {
|
||||||
|
// We need to break these down individual because addOption doesn't
|
||||||
|
// support all types.
|
||||||
|
step.addOption(bool, "flatpak", self.flatpak);
|
||||||
|
step.addOption(bool, "adwaita", self.adwaita);
|
||||||
|
step.addOption(bool, "x11", self.x11);
|
||||||
|
step.addOption(bool, "wayland", self.wayland);
|
||||||
|
step.addOption(bool, "sentry", self.sentry);
|
||||||
|
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
|
||||||
|
step.addOption(font.Backend, "font_backend", self.font_backend);
|
||||||
|
step.addOption(renderer.Impl, "renderer", self.renderer);
|
||||||
|
step.addOption(ExeEntrypoint, "exe_entrypoint", self.exe_entrypoint);
|
||||||
|
step.addOption(WasmTarget, "wasm_target", self.wasm_target);
|
||||||
|
step.addOption(bool, "wasm_shared", self.wasm_shared);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
step.addOption(std.SemanticVersion, "app_version", self.version);
|
||||||
|
step.addOption([:0]const u8, "app_version_string", try std.fmt.bufPrintZ(
|
||||||
|
&buf,
|
||||||
|
"{}",
|
||||||
|
.{self.version},
|
||||||
|
));
|
||||||
|
step.addOption(
|
||||||
|
ReleaseChannel,
|
||||||
|
"release_channel",
|
||||||
|
channel: {
|
||||||
|
const pre = self.version.pre orelse break :channel .stable;
|
||||||
|
if (pre.len == 0) break :channel .stable;
|
||||||
|
break :channel .tip;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rehydrate our Config from the comptime options. Note that not all
|
||||||
|
/// options are available at comptime, so look closely at this implementation
|
||||||
|
/// to see what is and isn't available.
|
||||||
|
pub fn fromOptions() Config {
|
||||||
|
const options = @import("build_options");
|
||||||
|
return .{
|
||||||
|
// Unused at runtime.
|
||||||
|
.optimize = undefined,
|
||||||
|
.target = undefined,
|
||||||
|
.env = undefined,
|
||||||
|
|
||||||
|
.version = options.app_version,
|
||||||
|
.flatpak = options.flatpak,
|
||||||
|
.adwaita = options.adwaita,
|
||||||
|
.app_runtime = std.meta.stringToEnum(apprt.Runtime, @tagName(options.app_runtime)).?,
|
||||||
|
.font_backend = std.meta.stringToEnum(font.Backend, @tagName(options.font_backend)).?,
|
||||||
|
.renderer = std.meta.stringToEnum(renderer.Impl, @tagName(options.renderer)).?,
|
||||||
|
.exe_entrypoint = std.meta.stringToEnum(ExeEntrypoint, @tagName(options.exe_entrypoint)).?,
|
||||||
|
.wasm_target = std.meta.stringToEnum(WasmTarget, @tagName(options.wasm_target)).?,
|
||||||
|
.wasm_shared = options.wasm_shared,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum OS version for the given OS tag. This shouldn't
|
||||||
|
/// be used generally, it should only be used for Darwin-based OS currently.
|
||||||
|
pub fn osVersionMin(tag: std.Target.Os.Tag) ?std.Target.Query.OsVersion {
|
||||||
|
return switch (tag) {
|
||||||
|
// We support back to the earliest officially supported version
|
||||||
|
// of macOS by Apple. EOL versions are not supported.
|
||||||
|
.macos => .{ .semver = .{
|
||||||
|
.major = 13,
|
||||||
|
.minor = 0,
|
||||||
|
.patch = 0,
|
||||||
|
} },
|
||||||
|
|
||||||
|
// iOS 17 picked arbitrarily
|
||||||
|
.ios => .{ .semver = .{
|
||||||
|
.major = 17,
|
||||||
|
.minor = 0,
|
||||||
|
.patch = 0,
|
||||||
|
} },
|
||||||
|
|
||||||
|
// This should never happen currently. If we add a new target then
|
||||||
|
// we should add a new case here.
|
||||||
|
else => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a ResolvedTarget for a mac with a `target.result.cpu.model.name` of `generic`.
|
||||||
|
// `b.standardTargetOptions()` returns a more specific cpu like `apple_a15`.
|
||||||
|
//
|
||||||
|
// This is used to workaround compilation issues on macOS.
|
||||||
|
// (see for example https://github.com/mitchellh/ghostty/issues/1640).
|
||||||
|
pub fn genericMacOSTarget(
|
||||||
|
b: *std.Build,
|
||||||
|
arch: ?std.Target.Cpu.Arch,
|
||||||
|
) std.Build.ResolvedTarget {
|
||||||
|
return b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = arch orelse builtin.target.cpu.arch,
|
||||||
|
.os_tag = .macos,
|
||||||
|
.os_version_min = osVersionMin(.macos),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The possible entrypoints for the exe artifact. This has no effect on
|
||||||
|
/// other artifact types (i.e. lib, wasm_module).
|
||||||
|
///
|
||||||
|
/// The whole existence of this enum is to workaround the fact that Zig
|
||||||
|
/// doesn't allow the main function to be in a file in a subdirctory
|
||||||
|
/// from the "root" of the module, and I don't want to pollute our root
|
||||||
|
/// directory with a bunch of individual zig files for each entrypoint.
|
||||||
|
///
|
||||||
|
/// Therefore, main.zig uses this to switch between the different entrypoints.
|
||||||
|
pub const ExeEntrypoint = enum {
|
||||||
|
ghostty,
|
||||||
|
helpgen,
|
||||||
|
mdgen_ghostty_1,
|
||||||
|
mdgen_ghostty_5,
|
||||||
|
webgen_config,
|
||||||
|
webgen_actions,
|
||||||
|
bench_parser,
|
||||||
|
bench_stream,
|
||||||
|
bench_codepoint_width,
|
||||||
|
bench_grapheme_break,
|
||||||
|
bench_page_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The release channel for the build.
|
||||||
|
pub const ReleaseChannel = enum {
|
||||||
|
/// Unstable builds on every commit.
|
||||||
|
tip,
|
||||||
|
|
||||||
|
/// Stable tagged releases.
|
||||||
|
stable,
|
||||||
|
};
|
69
src/build/GhosttyBench.zig
Normal file
69
src/build/GhosttyBench.zig
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//! GhosttyBench generates all the Ghostty benchmark helper binaries.
|
||||||
|
const GhosttyBench = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const SharedDeps = @import("SharedDeps.zig");
|
||||||
|
|
||||||
|
steps: []*std.Build.Step.Compile,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
b: *std.Build,
|
||||||
|
deps: *const SharedDeps,
|
||||||
|
) !GhosttyBench {
|
||||||
|
var steps = std.ArrayList(*std.Build.Step.Compile).init(b.allocator);
|
||||||
|
errdefer steps.deinit();
|
||||||
|
|
||||||
|
// Open the directory ./src/bench
|
||||||
|
const c_dir_path = b.pathFromRoot("src/bench");
|
||||||
|
var c_dir = try std.fs.cwd().openDir(c_dir_path, .{ .iterate = true });
|
||||||
|
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];
|
||||||
|
|
||||||
|
// 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 = b.path("src/main.zig"),
|
||||||
|
.target = deps.config.target,
|
||||||
|
|
||||||
|
// We always want our benchmarks to be in release mode.
|
||||||
|
.optimize = .ReleaseFast,
|
||||||
|
});
|
||||||
|
c_exe.linkLibC();
|
||||||
|
|
||||||
|
// Update our entrypoint
|
||||||
|
var enum_name: [64]u8 = undefined;
|
||||||
|
@memcpy(enum_name[0..name.len], name);
|
||||||
|
std.mem.replaceScalar(u8, enum_name[0..name.len], '-', '_');
|
||||||
|
|
||||||
|
var buf: [64]u8 = undefined;
|
||||||
|
const new_deps = try deps.changeEntrypoint(b, std.meta.stringToEnum(
|
||||||
|
Config.ExeEntrypoint,
|
||||||
|
try std.fmt.bufPrint(&buf, "bench_{s}", .{enum_name[0..name.len]}),
|
||||||
|
).?);
|
||||||
|
|
||||||
|
_ = try new_deps.add(c_exe);
|
||||||
|
|
||||||
|
try steps.append(c_exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .steps = steps.items };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyBench) void {
|
||||||
|
const b = self.steps[0].step.owner;
|
||||||
|
for (self.steps) |step| b.installArtifact(step);
|
||||||
|
}
|
92
src/build/GhosttyDocs.zig
Normal file
92
src/build/GhosttyDocs.zig
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
//! GhosttyDocs generates all the on-disk documentation that Ghostty is
|
||||||
|
//! installed with (man pages, html, markdown, etc.)
|
||||||
|
const GhosttyDocs = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const SharedDeps = @import("SharedDeps.zig");
|
||||||
|
|
||||||
|
steps: []*std.Build.Step,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
b: *std.Build,
|
||||||
|
deps: *const SharedDeps,
|
||||||
|
) !GhosttyDocs {
|
||||||
|
var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
|
||||||
|
errdefer steps.deinit();
|
||||||
|
|
||||||
|
const manpages = [_]struct {
|
||||||
|
name: []const u8,
|
||||||
|
section: []const u8,
|
||||||
|
}{
|
||||||
|
.{ .name = "ghostty", .section = "1" },
|
||||||
|
.{ .name = "ghostty", .section = "5" },
|
||||||
|
};
|
||||||
|
|
||||||
|
inline for (manpages) |manpage| {
|
||||||
|
const generate_markdown = b.addExecutable(.{
|
||||||
|
.name = "mdgen_" ++ manpage.name ++ "_" ++ manpage.section,
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
deps.help_strings.addImport(generate_markdown);
|
||||||
|
|
||||||
|
const gen_config = config: {
|
||||||
|
var copy = deps.config.*;
|
||||||
|
copy.exe_entrypoint = @field(
|
||||||
|
Config.ExeEntrypoint,
|
||||||
|
"mdgen_" ++ manpage.name ++ "_" ++ manpage.section,
|
||||||
|
);
|
||||||
|
break :config copy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const generate_markdown_options = b.addOptions();
|
||||||
|
try gen_config.addOptions(generate_markdown_options);
|
||||||
|
generate_markdown.root_module.addOptions("build_options", generate_markdown_options);
|
||||||
|
|
||||||
|
const generate_markdown_step = b.addRunArtifact(generate_markdown);
|
||||||
|
const markdown_output = generate_markdown_step.captureStdOut();
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
markdown_output,
|
||||||
|
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".md",
|
||||||
|
).step);
|
||||||
|
|
||||||
|
const generate_html = b.addSystemCommand(&.{"pandoc"});
|
||||||
|
generate_html.addArgs(&.{
|
||||||
|
"--standalone",
|
||||||
|
"--from",
|
||||||
|
"markdown",
|
||||||
|
"--to",
|
||||||
|
"html",
|
||||||
|
});
|
||||||
|
generate_html.addFileArg(markdown_output);
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
generate_html.captureStdOut(),
|
||||||
|
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".html",
|
||||||
|
).step);
|
||||||
|
|
||||||
|
const generate_manpage = b.addSystemCommand(&.{"pandoc"});
|
||||||
|
generate_manpage.addArgs(&.{
|
||||||
|
"--standalone",
|
||||||
|
"--from",
|
||||||
|
"markdown",
|
||||||
|
"--to",
|
||||||
|
"man",
|
||||||
|
});
|
||||||
|
generate_manpage.addFileArg(markdown_output);
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
generate_manpage.captureStdOut(),
|
||||||
|
"share/man/man" ++ manpage.section ++ "/" ++ manpage.name ++ "." ++ manpage.section,
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .steps = steps.items };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyDocs) void {
|
||||||
|
const b = self.steps[0].owner;
|
||||||
|
for (self.steps) |step| b.getInstallStep().dependOn(step);
|
||||||
|
}
|
120
src/build/GhosttyExe.zig
Normal file
120
src/build/GhosttyExe.zig
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
const Ghostty = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const SharedDeps = @import("SharedDeps.zig");
|
||||||
|
|
||||||
|
/// The primary Ghostty executable.
|
||||||
|
exe: *std.Build.Step.Compile,
|
||||||
|
|
||||||
|
/// The install step for the executable.
|
||||||
|
install_step: *std.Build.Step.InstallArtifact,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty {
|
||||||
|
const exe: *std.Build.Step.Compile = b.addExecutable(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = cfg.target,
|
||||||
|
.optimize = cfg.optimize,
|
||||||
|
.strip = cfg.strip,
|
||||||
|
});
|
||||||
|
const install_step = b.addInstallArtifact(exe, .{});
|
||||||
|
|
||||||
|
// Set PIE if requested
|
||||||
|
if (cfg.pie) exe.pie = true;
|
||||||
|
|
||||||
|
// Add the shared dependencies
|
||||||
|
_ = try deps.add(exe);
|
||||||
|
|
||||||
|
// Check for possible issues
|
||||||
|
try checkNixShell(exe, cfg);
|
||||||
|
|
||||||
|
// Patch our rpath if that option is specified.
|
||||||
|
if (cfg.patch_rpath) |rpath| {
|
||||||
|
if (rpath.len > 0) {
|
||||||
|
const run = std.Build.Step.Run.create(b, "patchelf rpath");
|
||||||
|
run.addArgs(&.{ "patchelf", "--set-rpath", rpath });
|
||||||
|
run.addArtifactArg(exe);
|
||||||
|
install_step.step.dependOn(&run.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OS-specific
|
||||||
|
switch (cfg.target.result.os.tag) {
|
||||||
|
.windows => {
|
||||||
|
exe.subsystem = .Windows;
|
||||||
|
exe.addWin32ResourceFile(.{
|
||||||
|
.file = b.path("dist/windows/ghostty.rc"),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.exe = exe,
|
||||||
|
.install_step = install_step,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the ghostty exe to the install target.
|
||||||
|
pub fn install(self: *const Ghostty) void {
|
||||||
|
const b = self.install_step.step.owner;
|
||||||
|
b.getInstallStep().dependOn(&self.install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If we're in NixOS but not in the shell environment then we issue
|
||||||
|
/// a warning because the rpath may not be setup properly. This doesn't modify
|
||||||
|
/// our build in any way but addresses a common build-from-source issue
|
||||||
|
/// for a subset of users.
|
||||||
|
fn checkNixShell(exe: *std.Build.Step.Compile, cfg: *const Config) !void {
|
||||||
|
// Non-Linux doesn't have rpath issues.
|
||||||
|
if (cfg.target.result.os.tag != .linux) return;
|
||||||
|
|
||||||
|
// When cross-compiling, we don't need to worry about matching our
|
||||||
|
// Nix shell rpath since the resulting binary will be run on a
|
||||||
|
// separate system.
|
||||||
|
if (!cfg.target.query.isNativeCpu()) return;
|
||||||
|
if (!cfg.target.query.isNativeOs()) return;
|
||||||
|
|
||||||
|
// Verify we're in NixOS
|
||||||
|
std.fs.accessAbsolute("/etc/NIXOS", .{}) catch return;
|
||||||
|
|
||||||
|
// If we're in a nix shell, not a problem
|
||||||
|
if (cfg.env.get("IN_NIX_SHELL") != null) return;
|
||||||
|
|
||||||
|
try exe.step.addError(
|
||||||
|
"\x1b[" ++ color_map.get("yellow").? ++
|
||||||
|
"\x1b[" ++ color_map.get("d").? ++
|
||||||
|
\\Detected building on and for NixOS outside of the Nix shell environment.
|
||||||
|
\\
|
||||||
|
\\The resulting ghostty binary will likely fail on launch because it is
|
||||||
|
\\unable to dynamically load the windowing libs (X11, Wayland, etc.).
|
||||||
|
\\We highly recommend running only within the Nix build environment
|
||||||
|
\\and the resulting binary will be portable across your system.
|
||||||
|
\\
|
||||||
|
\\To run in the Nix build environment, use the following command.
|
||||||
|
\\Append any additional options like (`-Doptimize` flags). The resulting
|
||||||
|
\\binary will be in zig-out as usual.
|
||||||
|
\\
|
||||||
|
\\ nix develop -c zig build
|
||||||
|
\\
|
||||||
|
++
|
||||||
|
"\x1b[0m",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ANSI escape codes for colored log output
|
||||||
|
const color_map = std.StaticStringMap([]const u8).initComptime(.{
|
||||||
|
&.{ "black", "30m" },
|
||||||
|
&.{ "blue", "34m" },
|
||||||
|
&.{ "b", "1m" },
|
||||||
|
&.{ "d", "2m" },
|
||||||
|
&.{ "cyan", "36m" },
|
||||||
|
&.{ "green", "32m" },
|
||||||
|
&.{ "magenta", "35m" },
|
||||||
|
&.{ "red", "31m" },
|
||||||
|
&.{ "white", "37m" },
|
||||||
|
&.{ "yellow", "33m" },
|
||||||
|
});
|
110
src/build/GhosttyLib.zig
Normal file
110
src/build/GhosttyLib.zig
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
const GhosttyLib = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
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.
|
||||||
|
step: *std.Build.Step,
|
||||||
|
|
||||||
|
/// The final static library file
|
||||||
|
output: std.Build.LazyPath,
|
||||||
|
|
||||||
|
pub fn initStatic(
|
||||||
|
b: *std.Build,
|
||||||
|
deps: *const SharedDeps,
|
||||||
|
) !GhosttyLib {
|
||||||
|
const lib = b.addStaticLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = b.path("src/main_c.zig"),
|
||||||
|
.target = deps.config.target,
|
||||||
|
.optimize = deps.config.optimize,
|
||||||
|
});
|
||||||
|
lib.bundle_compiler_rt = true;
|
||||||
|
lib.linkLibC();
|
||||||
|
|
||||||
|
// Add our dependencies. Get the list of all static deps so we can
|
||||||
|
// build a combined archive if necessary.
|
||||||
|
var lib_list = try deps.add(lib);
|
||||||
|
try lib_list.append(lib.getEmittedBin());
|
||||||
|
|
||||||
|
if (!deps.config.target.result.isDarwin()) return .{
|
||||||
|
.step = &lib.step,
|
||||||
|
.output = lib.getEmittedBin(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.step = libtool.step,
|
||||||
|
.output = libtool.output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initShared(
|
||||||
|
b: *std.Build,
|
||||||
|
deps: *const SharedDeps,
|
||||||
|
) !GhosttyLib {
|
||||||
|
const lib = b.addSharedLibrary(.{
|
||||||
|
.name = "ghostty",
|
||||||
|
.root_source_file = b.path("src/main_c.zig"),
|
||||||
|
.target = deps.config.target,
|
||||||
|
.optimize = deps.config.optimize,
|
||||||
|
.strip = deps.config.strip,
|
||||||
|
});
|
||||||
|
_ = try deps.add(lib);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.step = &lib.step,
|
||||||
|
.output = lib.getEmittedBin(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initMacOSUniversal(
|
||||||
|
b: *std.Build,
|
||||||
|
original_deps: *const SharedDeps,
|
||||||
|
) !GhosttyLib {
|
||||||
|
const aarch64 = try initStatic(b, &try original_deps.retarget(
|
||||||
|
b,
|
||||||
|
Config.genericMacOSTarget(b, .aarch64),
|
||||||
|
));
|
||||||
|
const x86_64 = try initStatic(b, &try original_deps.retarget(
|
||||||
|
b,
|
||||||
|
Config.genericMacOSTarget(b, .x86_64),
|
||||||
|
));
|
||||||
|
|
||||||
|
const universal = LipoStep.create(b, .{
|
||||||
|
.name = "ghostty",
|
||||||
|
.out_name = "libghostty.a",
|
||||||
|
.input_a = aarch64.output,
|
||||||
|
.input_b = x86_64.output,
|
||||||
|
});
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.step = universal.step,
|
||||||
|
.output = universal.output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyLib, name: []const u8) void {
|
||||||
|
const b = self.step.owner;
|
||||||
|
const lib_install = b.addInstallLibFile(self.output, name);
|
||||||
|
b.getInstallStep().dependOn(&lib_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn installHeader(self: *const GhosttyLib) void {
|
||||||
|
const b = self.step.owner;
|
||||||
|
const header_install = b.addInstallHeaderFile(
|
||||||
|
b.path("include/ghostty.h"),
|
||||||
|
"ghostty.h",
|
||||||
|
);
|
||||||
|
b.getInstallStep().dependOn(&header_install.step);
|
||||||
|
}
|
257
src/build/GhosttyResources.zig
Normal file
257
src/build/GhosttyResources.zig
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
const GhosttyResources = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const buildpkg = @import("main.zig");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const config_vim = @import("../config/vim.zig");
|
||||||
|
const config_sublime_syntax = @import("../config/sublime_syntax.zig");
|
||||||
|
const terminfo = @import("../terminfo/main.zig");
|
||||||
|
const RunStep = std.Build.Step.Run;
|
||||||
|
|
||||||
|
steps: []*std.Build.Step,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
||||||
|
var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
|
||||||
|
errdefer steps.deinit();
|
||||||
|
|
||||||
|
// Terminfo
|
||||||
|
terminfo: {
|
||||||
|
// Encode our terminfo
|
||||||
|
var str = std.ArrayList(u8).init(b.allocator);
|
||||||
|
defer str.deinit();
|
||||||
|
try terminfo.ghostty.encode(str.writer());
|
||||||
|
|
||||||
|
// Write it
|
||||||
|
var wf = b.addWriteFiles();
|
||||||
|
const src_source = wf.add("share/terminfo/ghostty.terminfo", str.items);
|
||||||
|
const src_install = b.addInstallFile(src_source, "share/terminfo/ghostty.terminfo");
|
||||||
|
try steps.append(&src_install.step);
|
||||||
|
|
||||||
|
// Windows doesn't have the binaries below.
|
||||||
|
if (cfg.target.result.os.tag == .windows) break :terminfo;
|
||||||
|
|
||||||
|
// Convert to termcap source format if thats helpful to people and
|
||||||
|
// install it. The resulting value here is the termcap source in case
|
||||||
|
// that is used for other commands.
|
||||||
|
{
|
||||||
|
const run_step = RunStep.create(b, "infotocap");
|
||||||
|
run_step.addArg("infotocap");
|
||||||
|
run_step.addFileArg(src_source);
|
||||||
|
const out_source = run_step.captureStdOut();
|
||||||
|
_ = run_step.captureStdErr(); // so we don't see stderr
|
||||||
|
|
||||||
|
const cap_install = b.addInstallFile(out_source, "share/terminfo/ghostty.termcap");
|
||||||
|
try steps.append(&cap_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile the terminfo source into a terminfo database
|
||||||
|
{
|
||||||
|
const run_step = RunStep.create(b, "tic");
|
||||||
|
run_step.addArgs(&.{ "tic", "-x", "-o" });
|
||||||
|
const path = run_step.addOutputFileArg("terminfo");
|
||||||
|
run_step.addFileArg(src_source);
|
||||||
|
_ = run_step.captureStdErr(); // so we don't see stderr
|
||||||
|
|
||||||
|
// Depend on the terminfo source install step so that Zig build
|
||||||
|
// creates the "share" directory for us.
|
||||||
|
run_step.step.dependOn(&src_install.step);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Use cp -R instead of Step.InstallDir because we need to preserve
|
||||||
|
// symlinks in the terminfo database. Zig's InstallDir step doesn't
|
||||||
|
// handle symlinks correctly yet.
|
||||||
|
const copy_step = RunStep.create(b, "copy terminfo db");
|
||||||
|
copy_step.addArgs(&.{ "cp", "-R" });
|
||||||
|
copy_step.addFileArg(path);
|
||||||
|
copy_step.addArg(b.fmt("{s}/share", .{b.install_path}));
|
||||||
|
try steps.append(©_step.step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shell-integration
|
||||||
|
{
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = b.path("src/shell-integration"),
|
||||||
|
.install_dir = .{ .custom = "share" },
|
||||||
|
.install_subdir = b.pathJoin(&.{ "ghostty", "shell-integration" }),
|
||||||
|
.exclude_extensions = &.{".md"},
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Themes
|
||||||
|
{
|
||||||
|
const upstream = b.dependency("iterm2_themes", .{});
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = upstream.path("ghostty"),
|
||||||
|
.install_dir = .{ .custom = "share" },
|
||||||
|
.install_subdir = b.pathJoin(&.{ "ghostty", "themes" }),
|
||||||
|
.exclude_extensions = &.{".md"},
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fish shell completions
|
||||||
|
{
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
_ = wf.add("ghostty.fish", buildpkg.fish_completions);
|
||||||
|
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = wf.getDirectory(),
|
||||||
|
.install_dir = .prefix,
|
||||||
|
.install_subdir = "share/fish/vendor_completions.d",
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// zsh shell completions
|
||||||
|
{
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
_ = wf.add("_ghostty", buildpkg.zsh_completions);
|
||||||
|
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = wf.getDirectory(),
|
||||||
|
.install_dir = .prefix,
|
||||||
|
.install_subdir = "share/zsh/site-functions",
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bash shell completions
|
||||||
|
{
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
_ = wf.add("ghostty.bash", buildpkg.bash_completions);
|
||||||
|
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = wf.getDirectory(),
|
||||||
|
.install_dir = .prefix,
|
||||||
|
.install_subdir = "share/bash-completion/completions",
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vim plugin
|
||||||
|
{
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
_ = wf.add("syntax/ghostty.vim", config_vim.syntax);
|
||||||
|
_ = wf.add("ftdetect/ghostty.vim", config_vim.ftdetect);
|
||||||
|
_ = wf.add("ftplugin/ghostty.vim", config_vim.ftplugin);
|
||||||
|
_ = wf.add("compiler/ghostty.vim", config_vim.compiler);
|
||||||
|
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = wf.getDirectory(),
|
||||||
|
.install_dir = .prefix,
|
||||||
|
.install_subdir = "share/vim/vimfiles",
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neovim plugin
|
||||||
|
// This is just a copy-paste of the Vim plugin, but using a Neovim subdir.
|
||||||
|
// By default, Neovim doesn't look inside share/vim/vimfiles. Some distros
|
||||||
|
// configure it to do that however. Fedora, does not as a counterexample.
|
||||||
|
{
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
_ = wf.add("syntax/ghostty.vim", config_vim.syntax);
|
||||||
|
_ = wf.add("ftdetect/ghostty.vim", config_vim.ftdetect);
|
||||||
|
_ = wf.add("ftplugin/ghostty.vim", config_vim.ftplugin);
|
||||||
|
_ = wf.add("compiler/ghostty.vim", config_vim.compiler);
|
||||||
|
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = wf.getDirectory(),
|
||||||
|
.install_dir = .prefix,
|
||||||
|
.install_subdir = "share/nvim/site",
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sublime syntax highlighting for bat cli tool
|
||||||
|
// NOTE: The current implementation requires symlinking the generated
|
||||||
|
// 'ghostty.sublime-syntax' file from zig-out to the '~.config/bat/syntaxes'
|
||||||
|
// directory. The syntax then needs to be mapped to the correct language in
|
||||||
|
// the config file within the '~.config/bat' directory
|
||||||
|
// (ex: --map-syntax "/Users/user/.config/ghostty/config:Ghostty Config").
|
||||||
|
{
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
_ = wf.add("ghostty.sublime-syntax", config_sublime_syntax.syntax);
|
||||||
|
|
||||||
|
const install_step = b.addInstallDirectory(.{
|
||||||
|
.source_dir = wf.getDirectory(),
|
||||||
|
.install_dir = .prefix,
|
||||||
|
.install_subdir = "share/bat/syntaxes",
|
||||||
|
});
|
||||||
|
try steps.append(&install_step.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// App (Linux)
|
||||||
|
if (cfg.target.result.os.tag == .linux) {
|
||||||
|
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||||||
|
|
||||||
|
// Desktop file so that we have an icon and other metadata
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("dist/linux/app.desktop"),
|
||||||
|
"share/applications/com.mitchellh.ghostty.desktop",
|
||||||
|
).step);
|
||||||
|
|
||||||
|
// Right click menu action for Plasma desktop
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("dist/linux/ghostty_dolphin.desktop"),
|
||||||
|
"share/kio/servicemenus/com.mitchellh.ghostty.desktop",
|
||||||
|
).step);
|
||||||
|
|
||||||
|
// Various icons that our application can use, including the icon
|
||||||
|
// that will be used for the desktop.
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_16.png"),
|
||||||
|
"share/icons/hicolor/16x16/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_32.png"),
|
||||||
|
"share/icons/hicolor/32x32/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_128.png"),
|
||||||
|
"share/icons/hicolor/128x128/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_256.png"),
|
||||||
|
"share/icons/hicolor/256x256/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_512.png"),
|
||||||
|
"share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
// Flatpaks only support icons up to 512x512.
|
||||||
|
if (!cfg.flatpak) {
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_1024.png"),
|
||||||
|
"share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_16@2x.png"),
|
||||||
|
"share/icons/hicolor/16x16@2/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_32@2x.png"),
|
||||||
|
"share/icons/hicolor/32x32@2/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_128@2x.png"),
|
||||||
|
"share/icons/hicolor/128x128@2/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
b.path("images/icons/icon_256@2x.png"),
|
||||||
|
"share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png",
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .steps = steps.items };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyResources) void {
|
||||||
|
const b = self.steps[0].owner;
|
||||||
|
for (self.steps) |step| b.getInstallStep().dependOn(step);
|
||||||
|
}
|
82
src/build/GhosttyWebdata.zig
Normal file
82
src/build/GhosttyWebdata.zig
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//! GhosttyWebdata generates all the Ghostty website data that is
|
||||||
|
//! merged with the website for things like config references.
|
||||||
|
const GhosttyWebdata = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const SharedDeps = @import("SharedDeps.zig");
|
||||||
|
|
||||||
|
steps: []*std.Build.Step,
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
b: *std.Build,
|
||||||
|
deps: *const SharedDeps,
|
||||||
|
) !GhosttyWebdata {
|
||||||
|
var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
|
||||||
|
errdefer steps.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
const webgen_config = b.addExecutable(.{
|
||||||
|
.name = "webgen_config",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
deps.help_strings.addImport(webgen_config);
|
||||||
|
|
||||||
|
{
|
||||||
|
const buildconfig = config: {
|
||||||
|
var copy = deps.config.*;
|
||||||
|
copy.exe_entrypoint = .webgen_config;
|
||||||
|
break :config copy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = b.addOptions();
|
||||||
|
try buildconfig.addOptions(options);
|
||||||
|
webgen_config.root_module.addOptions("build_options", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const webgen_config_step = b.addRunArtifact(webgen_config);
|
||||||
|
const webgen_config_out = webgen_config_step.captureStdOut();
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
webgen_config_out,
|
||||||
|
"share/ghostty/webdata/config.mdx",
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const webgen_actions = b.addExecutable(.{
|
||||||
|
.name = "webgen_actions",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
deps.help_strings.addImport(webgen_actions);
|
||||||
|
|
||||||
|
{
|
||||||
|
const buildconfig = config: {
|
||||||
|
var copy = deps.config.*;
|
||||||
|
copy.exe_entrypoint = .webgen_actions;
|
||||||
|
break :config copy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = b.addOptions();
|
||||||
|
try buildconfig.addOptions(options);
|
||||||
|
webgen_actions.root_module.addOptions("build_options", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const webgen_actions_step = b.addRunArtifact(webgen_actions);
|
||||||
|
const webgen_actions_out = webgen_actions_step.captureStdOut();
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
webgen_actions_out,
|
||||||
|
"share/ghostty/webdata/actions.mdx",
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .steps = steps.items };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyWebdata) void {
|
||||||
|
const b = self.steps[0].owner;
|
||||||
|
for (self.steps) |step| b.getInstallStep().dependOn(step);
|
||||||
|
}
|
68
src/build/GhosttyXCFramework.zig
Normal file
68
src/build/GhosttyXCFramework.zig
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
const GhosttyXCFramework = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const SharedDeps = @import("SharedDeps.zig");
|
||||||
|
const GhosttyLib = @import("GhosttyLib.zig");
|
||||||
|
const XCFrameworkStep = @import("XCFrameworkStep.zig");
|
||||||
|
|
||||||
|
xcframework: *XCFrameworkStep,
|
||||||
|
macos: GhosttyLib,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build, deps: *const SharedDeps) !GhosttyXCFramework {
|
||||||
|
// Create our universal macOS static library.
|
||||||
|
const macos = try GhosttyLib.initMacOSUniversal(b, deps);
|
||||||
|
|
||||||
|
// iOS
|
||||||
|
const ios = try GhosttyLib.initStatic(b, &try deps.retarget(
|
||||||
|
b,
|
||||||
|
b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = .aarch64,
|
||||||
|
.os_tag = .ios,
|
||||||
|
.os_version_min = Config.osVersionMin(.ios),
|
||||||
|
.abi = null,
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
|
// iOS Simulator
|
||||||
|
const ios_sim = try GhosttyLib.initStatic(b, &try deps.retarget(
|
||||||
|
b,
|
||||||
|
b.resolveTargetQuery(.{
|
||||||
|
.cpu_arch = .aarch64,
|
||||||
|
.os_tag = .ios,
|
||||||
|
.os_version_min = Config.osVersionMin(.ios),
|
||||||
|
.abi = .simulator,
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
.libraries = &.{
|
||||||
|
.{
|
||||||
|
.library = macos.output,
|
||||||
|
.headers = b.path("include"),
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.library = ios.output,
|
||||||
|
.headers = b.path("include"),
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.library = ios_sim.output,
|
||||||
|
.headers = b.path("include"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.xcframework = xcframework,
|
||||||
|
.macos = macos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyXCFramework) void {
|
||||||
|
const b = self.xcframework.step.owner;
|
||||||
|
b.getInstallStep().dependOn(self.xcframework.step);
|
||||||
|
}
|
46
src/build/HelpStrings.zig
Normal file
46
src/build/HelpStrings.zig
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const HelpStrings = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
|
||||||
|
/// The "helpgen" exe.
|
||||||
|
exe: *std.Build.Step.Compile,
|
||||||
|
|
||||||
|
/// The output path for the help strings.
|
||||||
|
output: std.Build.LazyPath,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build, cfg: *const Config) !HelpStrings {
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "helpgen",
|
||||||
|
.root_source_file = b.path("src/helpgen.zig"),
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
|
||||||
|
const help_config = config: {
|
||||||
|
var copy = cfg.*;
|
||||||
|
copy.exe_entrypoint = .helpgen;
|
||||||
|
break :config copy;
|
||||||
|
};
|
||||||
|
const options = b.addOptions();
|
||||||
|
try help_config.addOptions(options);
|
||||||
|
exe.root_module.addOptions("build_options", options);
|
||||||
|
|
||||||
|
const help_run = b.addRunArtifact(exe);
|
||||||
|
return .{
|
||||||
|
.exe = exe,
|
||||||
|
.output = help_run.captureStdOut(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the "help_strings" import.
|
||||||
|
pub fn addImport(self: *const HelpStrings, step: *std.Build.Step.Compile) void {
|
||||||
|
self.output.addStepDependencies(&step.step);
|
||||||
|
step.root_module.addAnonymousImport("help_strings", .{
|
||||||
|
.root_source_file = self.output,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install the help exe
|
||||||
|
pub fn install(self: *const HelpStrings) void {
|
||||||
|
self.exe.step.owner.installArtifact(self.exe);
|
||||||
|
}
|
@ -21,13 +21,13 @@ pub const Options = struct {
|
|||||||
step: *Step,
|
step: *Step,
|
||||||
output: LazyPath,
|
output: LazyPath,
|
||||||
|
|
||||||
pub fn create(b: *std.Build, opts: Options) *MetallibStep {
|
pub fn create(b: *std.Build, opts: Options) ?*MetallibStep {
|
||||||
const self = b.allocator.create(MetallibStep) catch @panic("OOM");
|
const self = b.allocator.create(MetallibStep) catch @panic("OOM");
|
||||||
|
|
||||||
const sdk = switch (opts.target.result.os.tag) {
|
const sdk = switch (opts.target.result.os.tag) {
|
||||||
.macos => "macosx",
|
.macos => "macosx",
|
||||||
.ios => "iphoneos",
|
.ios => "iphoneos",
|
||||||
else => @panic("unsupported metallib OS"),
|
else => return null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const min_version = if (opts.target.query.os_version_min) |v|
|
const min_version = if (opts.target.query.os_version_min) |v|
|
||||||
|
510
src/build/SharedDeps.zig
Normal file
510
src/build/SharedDeps.zig
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
const SharedDeps = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Scanner = @import("zig_wayland").Scanner;
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const HelpStrings = @import("HelpStrings.zig");
|
||||||
|
const MetallibStep = @import("MetallibStep.zig");
|
||||||
|
const UnicodeTables = @import("UnicodeTables.zig");
|
||||||
|
|
||||||
|
config: *const Config,
|
||||||
|
|
||||||
|
options: *std.Build.Step.Options,
|
||||||
|
help_strings: HelpStrings,
|
||||||
|
metallib: ?*MetallibStep,
|
||||||
|
unicode_tables: UnicodeTables,
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
var result: SharedDeps = .{
|
||||||
|
.config = cfg,
|
||||||
|
.help_strings = try HelpStrings.init(b, cfg),
|
||||||
|
.unicode_tables = try UnicodeTables.init(b),
|
||||||
|
|
||||||
|
// Setup by retarget
|
||||||
|
.options = undefined,
|
||||||
|
.metallib = undefined,
|
||||||
|
};
|
||||||
|
try result.initTarget(b, cfg.target);
|
||||||
|
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;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initTarget(
|
||||||
|
self: *SharedDeps,
|
||||||
|
b: *std.Build,
|
||||||
|
target: std.Build.ResolvedTarget,
|
||||||
|
) !void {
|
||||||
|
// Update our metallib
|
||||||
|
self.metallib = MetallibStep.create(b, .{
|
||||||
|
.name = "Ghostty",
|
||||||
|
.target = target,
|
||||||
|
.sources = &.{b.path("src/renderer/shaders/cell.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.init(b.allocator);
|
||||||
|
errdefer static_libs.deinit();
|
||||||
|
|
||||||
|
// Every exe gets build options populated
|
||||||
|
step.root_module.addOptions("build_options", self.options);
|
||||||
|
|
||||||
|
// Freetype
|
||||||
|
_ = b.systemIntegrationOption("freetype", .{}); // Shows it in help
|
||||||
|
if (self.config.font_backend.hasFreetype()) {
|
||||||
|
const freetype_dep = b.dependency("freetype", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.@"enable-libpng" = true,
|
||||||
|
});
|
||||||
|
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(freetype_dep.artifact("freetype").getEmittedBin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Harfbuzz
|
||||||
|
_ = b.systemIntegrationOption("harfbuzz", .{}); // Shows it in help
|
||||||
|
if (self.config.font_backend.hasHarfbuzz()) {
|
||||||
|
const harfbuzz_dep = b.dependency("harfbuzz", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.@"enable-freetype" = true,
|
||||||
|
.@"enable-coretext" = self.config.font_backend.hasCoretext(),
|
||||||
|
});
|
||||||
|
|
||||||
|
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(harfbuzz_dep.artifact("harfbuzz").getEmittedBin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fontconfig
|
||||||
|
_ = b.systemIntegrationOption("fontconfig", .{}); // Shows it in help
|
||||||
|
if (self.config.font_backend.hasFontconfig()) {
|
||||||
|
const fontconfig_dep = b.dependency("fontconfig", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
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(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", .{})) {
|
||||||
|
const libpng_dep = b.dependency("libpng", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.linkLibrary(libpng_dep.artifact("png"));
|
||||||
|
try static_libs.append(libpng_dep.artifact("png").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zlib - same as libpng, only used through dependencies.
|
||||||
|
if (!b.systemIntegrationOption("zlib", .{})) {
|
||||||
|
const zlib_dep = b.dependency("zlib", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.linkLibrary(zlib_dep.artifact("z"));
|
||||||
|
try static_libs.append(zlib_dep.artifact("z").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oniguruma
|
||||||
|
const oniguruma_dep = b.dependency("oniguruma", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
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(oniguruma_dep.artifact("oniguruma").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glslang
|
||||||
|
const glslang_dep = b.dependency("glslang", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
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(glslang_dep.artifact("glslang").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spirv-cross
|
||||||
|
const spirv_cross_dep = b.dependency("spirv_cross", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross"));
|
||||||
|
if (b.systemIntegrationOption("spirv-cross", .{})) {
|
||||||
|
step.linkSystemLibrary2("spirv-cross", dynamic_link_opts);
|
||||||
|
} else {
|
||||||
|
step.linkLibrary(spirv_cross_dep.artifact("spirv_cross"));
|
||||||
|
try static_libs.append(spirv_cross_dep.artifact("spirv_cross").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simdutf
|
||||||
|
if (b.systemIntegrationOption("simdutf", .{})) {
|
||||||
|
step.linkSystemLibrary2("simdutf", dynamic_link_opts);
|
||||||
|
} else {
|
||||||
|
const simdutf_dep = b.dependency("simdutf", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.linkLibrary(simdutf_dep.artifact("simdutf"));
|
||||||
|
try static_libs.append(simdutf_dep.artifact("simdutf").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sentry
|
||||||
|
if (self.config.sentry) {
|
||||||
|
const sentry_dep = b.dependency("sentry", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.backend = .breakpad,
|
||||||
|
});
|
||||||
|
|
||||||
|
step.root_module.addImport("sentry", sentry_dep.module("sentry"));
|
||||||
|
|
||||||
|
// Sentry
|
||||||
|
step.linkLibrary(sentry_dep.artifact("sentry"));
|
||||||
|
try static_libs.append(sentry_dep.artifact("sentry").getEmittedBin());
|
||||||
|
|
||||||
|
// We also need to include breakpad in the static libs.
|
||||||
|
const breakpad_dep = sentry_dep.builder.dependency("breakpad", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
try static_libs.append(breakpad_dep.artifact("breakpad").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wasm we do manually since it is such a different build.
|
||||||
|
if (step.rootModuleTarget().cpu.arch == .wasm32) {
|
||||||
|
const js_dep = b.dependency("zig_js", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
step.addLibraryPath(.{ .cwd_relative = b.fmt("/usr/lib/{s}", .{triple}) });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++ files
|
||||||
|
step.linkLibCpp();
|
||||||
|
step.addIncludePath(b.path("src"));
|
||||||
|
{
|
||||||
|
// From hwy/detect_targets.h
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3;
|
||||||
|
|
||||||
|
step.addCSourceFiles(.{
|
||||||
|
.files = &.{
|
||||||
|
"src/simd/base64.cpp",
|
||||||
|
"src/simd/codepoint_width.cpp",
|
||||||
|
"src/simd/index_of.cpp",
|
||||||
|
"src/simd/vt.cpp",
|
||||||
|
},
|
||||||
|
.flags = if (step.rootModuleTarget().cpu.arch == .x86_64) &.{
|
||||||
|
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
|
||||||
|
} else &.{},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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().isDarwin()) {
|
||||||
|
try @import("apple_sdk").addPaths(b, &step.root_module);
|
||||||
|
|
||||||
|
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
|
||||||
|
step.root_module.addImport("opengl", b.dependency(
|
||||||
|
"opengl",
|
||||||
|
.{},
|
||||||
|
).module("opengl"));
|
||||||
|
step.root_module.addImport("vaxis", b.dependency("vaxis", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}).module("vaxis"));
|
||||||
|
step.root_module.addImport("wuffs", b.dependency("wuffs", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}).module("wuffs"));
|
||||||
|
step.root_module.addImport("xev", b.dependency("libxev", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}).module("xev"));
|
||||||
|
step.root_module.addImport("z2d", b.addModule("z2d", .{
|
||||||
|
.root_source_file = b.dependency("z2d", .{}).path("src/z2d.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}));
|
||||||
|
step.root_module.addImport("ziglyph", b.dependency("ziglyph", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}).module("ziglyph"));
|
||||||
|
step.root_module.addImport("zf", b.dependency("zf", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.with_tui = false,
|
||||||
|
}).module("zf"));
|
||||||
|
|
||||||
|
// Mac Stuff
|
||||||
|
if (step.rootModuleTarget().isDarwin()) {
|
||||||
|
const objc_dep = b.dependency("zig_objc", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
const macos_dep = b.dependency("macos", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
step.root_module.addImport("objc", objc_dep.module("objc"));
|
||||||
|
step.root_module.addImport("macos", macos_dep.module("macos"));
|
||||||
|
step.linkLibrary(macos_dep.artifact("macos"));
|
||||||
|
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());
|
||||||
|
|
||||||
|
if (self.config.renderer == .opengl) {
|
||||||
|
step.linkFramework("OpenGL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cimgui
|
||||||
|
const cimgui_dep = b.dependency("cimgui", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.root_module.addImport("cimgui", cimgui_dep.module("cimgui"));
|
||||||
|
step.linkLibrary(cimgui_dep.artifact("cimgui"));
|
||||||
|
try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin());
|
||||||
|
|
||||||
|
// Highway
|
||||||
|
const highway_dep = b.dependency("highway", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.linkLibrary(highway_dep.artifact("highway"));
|
||||||
|
try static_libs.append(highway_dep.artifact("highway").getEmittedBin());
|
||||||
|
|
||||||
|
// utfcpp - This is used as a dependency on our hand-written C++ code
|
||||||
|
const utfcpp_dep = b.dependency("utfcpp", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
step.linkLibrary(utfcpp_dep.artifact("utfcpp"));
|
||||||
|
try static_libs.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
|
||||||
|
|
||||||
|
// 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 => {},
|
||||||
|
|
||||||
|
.glfw => glfw: {
|
||||||
|
const mach_glfw_dep = b.lazyDependency("mach_glfw", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
}) orelse break :glfw;
|
||||||
|
step.root_module.addImport("glfw", mach_glfw_dep.module("mach-glfw"));
|
||||||
|
},
|
||||||
|
|
||||||
|
.gtk => {
|
||||||
|
step.linkSystemLibrary2("gtk4", dynamic_link_opts);
|
||||||
|
if (self.config.adwaita) step.linkSystemLibrary2("adwaita-1", dynamic_link_opts);
|
||||||
|
if (self.config.x11) step.linkSystemLibrary2("X11", dynamic_link_opts);
|
||||||
|
|
||||||
|
if (self.config.wayland) {
|
||||||
|
const scanner = Scanner.create(b, .{
|
||||||
|
// We shouldn't be using getPath but we need to for now
|
||||||
|
// https://codeberg.org/ifreund/zig-wayland/issues/66
|
||||||
|
.wayland_xml_path = b.dependency("wayland", .{})
|
||||||
|
.path("protocol/wayland.xml")
|
||||||
|
.getPath(b),
|
||||||
|
.wayland_protocols_path = b.dependency("wayland_protocols", .{})
|
||||||
|
.path("")
|
||||||
|
.getPath(b),
|
||||||
|
});
|
||||||
|
|
||||||
|
const wayland = b.createModule(.{ .root_source_file = scanner.result });
|
||||||
|
|
||||||
|
const plasma_wayland_protocols = b.dependency("plasma_wayland_protocols", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
scanner.addCustomProtocol(plasma_wayland_protocols.path("src/protocols/blur.xml"));
|
||||||
|
|
||||||
|
scanner.generate("wl_compositor", 1);
|
||||||
|
scanner.generate("org_kde_kwin_blur_manager", 1);
|
||||||
|
|
||||||
|
step.root_module.addImport("wayland", wayland);
|
||||||
|
step.linkSystemLibrary2("wayland-client", dynamic_link_opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const gresource = @import("../apprt/gtk/gresource.zig");
|
||||||
|
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
const gresource_xml = wf.add("gresource.xml", gresource.gresource_xml);
|
||||||
|
|
||||||
|
const generate_resources_c = b.addSystemCommand(&.{
|
||||||
|
"glib-compile-resources",
|
||||||
|
"--c-name",
|
||||||
|
"ghostty",
|
||||||
|
"--generate-source",
|
||||||
|
"--target",
|
||||||
|
});
|
||||||
|
const ghostty_resources_c = generate_resources_c.addOutputFileArg("ghostty_resources.c");
|
||||||
|
generate_resources_c.addFileArg(gresource_xml);
|
||||||
|
generate_resources_c.extra_file_dependencies = &gresource.dependencies;
|
||||||
|
step.addCSourceFile(.{ .file = ghostty_resources_c, .flags = &.{} });
|
||||||
|
|
||||||
|
const generate_resources_h = b.addSystemCommand(&.{
|
||||||
|
"glib-compile-resources",
|
||||||
|
"--c-name",
|
||||||
|
"ghostty",
|
||||||
|
"--generate-header",
|
||||||
|
"--target",
|
||||||
|
});
|
||||||
|
const ghostty_resources_h = generate_resources_h.addOutputFileArg("ghostty_resources.h");
|
||||||
|
generate_resources_h.addFileArg(gresource_xml);
|
||||||
|
generate_resources_h.extra_file_dependencies = &gresource.dependencies;
|
||||||
|
step.addIncludePath(ghostty_resources_h.dirname());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.help_strings.addImport(step);
|
||||||
|
self.unicode_tables.addImport(step);
|
||||||
|
|
||||||
|
return static_libs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
43
src/build/UnicodeTables.zig
Normal file
43
src/build/UnicodeTables.zig
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const UnicodeTables = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
|
||||||
|
/// The exe.
|
||||||
|
exe: *std.Build.Step.Compile,
|
||||||
|
|
||||||
|
/// The output path for the unicode tables
|
||||||
|
output: std.Build.LazyPath,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build) !UnicodeTables {
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "unigen",
|
||||||
|
.root_source_file = b.path("src/unicode/props.zig"),
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
exe.linkLibC();
|
||||||
|
|
||||||
|
const ziglyph_dep = b.dependency("ziglyph", .{
|
||||||
|
.target = b.host,
|
||||||
|
});
|
||||||
|
exe.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
|
||||||
|
|
||||||
|
const run = b.addRunArtifact(exe);
|
||||||
|
return .{
|
||||||
|
.exe = exe,
|
||||||
|
.output = run.captureStdOut(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the "unicode_tables" import.
|
||||||
|
pub fn addImport(self: *const UnicodeTables, step: *std.Build.Step.Compile) void {
|
||||||
|
self.output.addStepDependencies(&step.step);
|
||||||
|
step.root_module.addAnonymousImport("unicode_tables", .{
|
||||||
|
.root_source_file = self.output,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install the exe
|
||||||
|
pub fn install(self: *const UnicodeTables, b: *std.Build) void {
|
||||||
|
b.installArtifact(self.exe);
|
||||||
|
}
|
@ -14,7 +14,7 @@ const Action = @import("../cli/action.zig").Action;
|
|||||||
/// it's part of an on going completion like --<key>=. Working around this requires looking
|
/// it's part of an on going completion like --<key>=. Working around this requires looking
|
||||||
/// backward in the command line args to pretend the = is an empty string
|
/// backward in the command line args to pretend the = is an empty string
|
||||||
/// see: https://www.gnu.org/software/gnuastro/manual/html_node/Bash-TAB-completion-tutorial.html
|
/// see: https://www.gnu.org/software/gnuastro/manual/html_node/Bash-TAB-completion-tutorial.html
|
||||||
pub const bash_completions = comptimeGenerateBashCompletions();
|
pub const completions = comptimeGenerateBashCompletions();
|
||||||
|
|
||||||
fn comptimeGenerateBashCompletions() []const u8 {
|
fn comptimeGenerateBashCompletions() []const u8 {
|
||||||
comptime {
|
comptime {
|
||||||
|
@ -5,7 +5,7 @@ const Action = @import("../cli/action.zig").Action;
|
|||||||
|
|
||||||
/// A fish completions configuration that contains all the available commands
|
/// A fish completions configuration that contains all the available commands
|
||||||
/// and options.
|
/// and options.
|
||||||
pub const fish_completions = comptimeGenerateFishCompletions();
|
pub const completions = comptimeGenerateFishCompletions();
|
||||||
|
|
||||||
fn comptimeGenerateFishCompletions() []const u8 {
|
fn comptimeGenerateFishCompletions() []const u8 {
|
||||||
comptime {
|
comptime {
|
||||||
|
24
src/build/gtk.zig
Normal file
24
src/build/gtk.zig
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Targets = packed struct {
|
||||||
|
x11: bool = false,
|
||||||
|
wayland: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns the targets that GTK4 was compiled with.
|
||||||
|
pub fn targets(b: *std.Build) Targets {
|
||||||
|
// Run pkg-config. We allow it to fail so that zig build --help
|
||||||
|
// works without all dependencies. The build will fail later when
|
||||||
|
// GTK isn't found anyways.
|
||||||
|
var code: u8 = undefined;
|
||||||
|
const output = b.runAllowFail(
|
||||||
|
&.{ "pkg-config", "--variable=targets", "gtk4" },
|
||||||
|
&code,
|
||||||
|
.Ignore,
|
||||||
|
) catch return .{};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.x11 = std.mem.indexOf(u8, output, "x11") != null,
|
||||||
|
.wayland = std.mem.indexOf(u8, output, "wayland") != null,
|
||||||
|
};
|
||||||
|
}
|
30
src/build/main.zig
Normal file
30
src/build/main.zig
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! Build logic for Ghostty. A single "build.zig" file became far too complex
|
||||||
|
//! and spaghetti, so this package extracts the build logic into smaller,
|
||||||
|
//! more manageable pieces.
|
||||||
|
|
||||||
|
pub const gtk = @import("gtk.zig");
|
||||||
|
pub const Config = @import("Config.zig");
|
||||||
|
pub const GitVersion = @import("GitVersion.zig");
|
||||||
|
|
||||||
|
// Artifacts
|
||||||
|
pub const GhosttyBench = @import("GhosttyBench.zig");
|
||||||
|
pub const GhosttyDocs = @import("GhosttyDocs.zig");
|
||||||
|
pub const GhosttyExe = @import("GhosttyExe.zig");
|
||||||
|
pub const GhosttyLib = @import("GhosttyLib.zig");
|
||||||
|
pub const GhosttyResources = @import("GhosttyResources.zig");
|
||||||
|
pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig");
|
||||||
|
pub const GhosttyWebdata = @import("GhosttyWebdata.zig");
|
||||||
|
pub const HelpStrings = @import("HelpStrings.zig");
|
||||||
|
pub const SharedDeps = @import("SharedDeps.zig");
|
||||||
|
pub const UnicodeTables = @import("UnicodeTables.zig");
|
||||||
|
|
||||||
|
// Steps
|
||||||
|
pub const LibtoolStep = @import("LibtoolStep.zig");
|
||||||
|
pub const LipoStep = @import("LipoStep.zig");
|
||||||
|
pub const MetallibStep = @import("MetallibStep.zig");
|
||||||
|
pub const XCFrameworkStep = @import("XCFrameworkStep.zig");
|
||||||
|
|
||||||
|
// Shell completions
|
||||||
|
pub const fish_completions = @import("fish_completions.zig").completions;
|
||||||
|
pub const zsh_completions = @import("zsh_completions.zig").completions;
|
||||||
|
pub const bash_completions = @import("bash_completions.zig").completions;
|
@ -5,7 +5,7 @@ const Action = @import("../cli/action.zig").Action;
|
|||||||
|
|
||||||
/// A zsh completions configuration that contains all the available commands
|
/// A zsh completions configuration that contains all the available commands
|
||||||
/// and options.
|
/// and options.
|
||||||
pub const zsh_completions = comptimeGenerateZshCompletions();
|
pub const completions = comptimeGenerateZshCompletions();
|
||||||
|
|
||||||
const equals_required = "=-:::";
|
const equals_required = "=-:::";
|
||||||
|
|
||||||
|
@ -10,88 +10,9 @@ const apprt = @import("apprt.zig");
|
|||||||
const font = @import("font/main.zig");
|
const font = @import("font/main.zig");
|
||||||
const rendererpkg = @import("renderer.zig");
|
const rendererpkg = @import("renderer.zig");
|
||||||
const WasmTarget = @import("os/wasm/target.zig").Target;
|
const WasmTarget = @import("os/wasm/target.zig").Target;
|
||||||
|
const BuildConfig = @import("build/Config.zig");
|
||||||
|
|
||||||
/// The build configurations options. This may not be all available options
|
pub const ReleaseChannel = BuildConfig.ReleaseChannel;
|
||||||
/// to `zig build` but it contains all the options that the Ghostty source
|
|
||||||
/// needs to know about at comptime.
|
|
||||||
///
|
|
||||||
/// We put this all in a single struct so that we can check compatibility
|
|
||||||
/// between options, make it easy to copy and mutate options for different
|
|
||||||
/// build types, etc.
|
|
||||||
pub const BuildConfig = struct {
|
|
||||||
version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
|
|
||||||
flatpak: bool = false,
|
|
||||||
adwaita: bool = false,
|
|
||||||
x11: bool = false,
|
|
||||||
wayland: bool = false,
|
|
||||||
sentry: bool = true,
|
|
||||||
app_runtime: apprt.Runtime = .none,
|
|
||||||
renderer: rendererpkg.Impl = .opengl,
|
|
||||||
font_backend: font.Backend = .freetype,
|
|
||||||
|
|
||||||
/// The entrypoint for exe targets.
|
|
||||||
exe_entrypoint: ExeEntrypoint = .ghostty,
|
|
||||||
|
|
||||||
/// The target runtime for the wasm build and whether to use wasm shared
|
|
||||||
/// memory or not. These are both legacy wasm-specific options that we
|
|
||||||
/// will probably have to revisit when we get back to work on wasm.
|
|
||||||
wasm_target: WasmTarget = .browser,
|
|
||||||
wasm_shared: bool = true,
|
|
||||||
|
|
||||||
/// Configure the build options with our values.
|
|
||||||
pub fn addOptions(self: BuildConfig, step: *std.Build.Step.Options) !void {
|
|
||||||
// We need to break these down individual because addOption doesn't
|
|
||||||
// support all types.
|
|
||||||
step.addOption(bool, "flatpak", self.flatpak);
|
|
||||||
step.addOption(bool, "adwaita", self.adwaita);
|
|
||||||
step.addOption(bool, "x11", self.x11);
|
|
||||||
step.addOption(bool, "wayland", self.wayland);
|
|
||||||
step.addOption(bool, "sentry", self.sentry);
|
|
||||||
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
|
|
||||||
step.addOption(font.Backend, "font_backend", self.font_backend);
|
|
||||||
step.addOption(rendererpkg.Impl, "renderer", self.renderer);
|
|
||||||
step.addOption(ExeEntrypoint, "exe_entrypoint", self.exe_entrypoint);
|
|
||||||
step.addOption(WasmTarget, "wasm_target", self.wasm_target);
|
|
||||||
step.addOption(bool, "wasm_shared", self.wasm_shared);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
step.addOption(std.SemanticVersion, "app_version", self.version);
|
|
||||||
step.addOption([:0]const u8, "app_version_string", try std.fmt.bufPrintZ(
|
|
||||||
&buf,
|
|
||||||
"{}",
|
|
||||||
.{self.version},
|
|
||||||
));
|
|
||||||
step.addOption(
|
|
||||||
ReleaseChannel,
|
|
||||||
"release_channel",
|
|
||||||
channel: {
|
|
||||||
const pre = self.version.pre orelse break :channel .stable;
|
|
||||||
if (pre.len == 0) break :channel .stable;
|
|
||||||
break :channel .tip;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rehydrate our BuildConfig from the comptime options. Note that not all
|
|
||||||
/// options are available at comptime, so look closely at this implementation
|
|
||||||
/// to see what is and isn't available.
|
|
||||||
pub fn fromOptions() BuildConfig {
|
|
||||||
return .{
|
|
||||||
.version = options.app_version,
|
|
||||||
.flatpak = options.flatpak,
|
|
||||||
.adwaita = options.adwaita,
|
|
||||||
.app_runtime = std.meta.stringToEnum(apprt.Runtime, @tagName(options.app_runtime)).?,
|
|
||||||
.font_backend = std.meta.stringToEnum(font.Backend, @tagName(options.font_backend)).?,
|
|
||||||
.renderer = std.meta.stringToEnum(rendererpkg.Impl, @tagName(options.renderer)).?,
|
|
||||||
.exe_entrypoint = std.meta.stringToEnum(ExeEntrypoint, @tagName(options.exe_entrypoint)).?,
|
|
||||||
.wasm_target = std.meta.stringToEnum(WasmTarget, @tagName(options.wasm_target)).?,
|
|
||||||
.wasm_shared = options.wasm_shared,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The semantic version of this build.
|
/// The semantic version of this build.
|
||||||
pub const version = options.app_version;
|
pub const version = options.app_version;
|
||||||
@ -114,7 +35,7 @@ pub const artifact = Artifact.detect();
|
|||||||
/// Our build configuration. We re-export a lot of these back at the
|
/// Our build configuration. We re-export a lot of these back at the
|
||||||
/// top-level so its a bit cleaner to use throughout the code. See the doc
|
/// top-level so its a bit cleaner to use throughout the code. See the doc
|
||||||
/// comments in BuildConfig for details on each.
|
/// comments in BuildConfig for details on each.
|
||||||
pub const config = BuildConfig.fromOptions();
|
const config = BuildConfig.fromOptions();
|
||||||
pub const exe_entrypoint = config.exe_entrypoint;
|
pub const exe_entrypoint = config.exe_entrypoint;
|
||||||
pub const flatpak = options.flatpak;
|
pub const flatpak = options.flatpak;
|
||||||
pub const app_runtime: apprt.Runtime = config.app_runtime;
|
pub const app_runtime: apprt.Runtime = config.app_runtime;
|
||||||
@ -175,35 +96,3 @@ pub const Artifact = enum {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The possible entrypoints for the exe artifact. This has no effect on
|
|
||||||
/// other artifact types (i.e. lib, wasm_module).
|
|
||||||
///
|
|
||||||
/// The whole existence of this enum is to workaround the fact that Zig
|
|
||||||
/// doesn't allow the main function to be in a file in a subdirctory
|
|
||||||
/// from the "root" of the module, and I don't want to pollute our root
|
|
||||||
/// directory with a bunch of individual zig files for each entrypoint.
|
|
||||||
///
|
|
||||||
/// Therefore, main.zig uses this to switch between the different entrypoints.
|
|
||||||
pub const ExeEntrypoint = enum {
|
|
||||||
ghostty,
|
|
||||||
helpgen,
|
|
||||||
mdgen_ghostty_1,
|
|
||||||
mdgen_ghostty_5,
|
|
||||||
webgen_config,
|
|
||||||
webgen_actions,
|
|
||||||
bench_parser,
|
|
||||||
bench_stream,
|
|
||||||
bench_codepoint_width,
|
|
||||||
bench_grapheme_break,
|
|
||||||
bench_page_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The release channel for the build.
|
|
||||||
pub const ReleaseChannel = enum {
|
|
||||||
/// Unstable builds on every commit.
|
|
||||||
tip,
|
|
||||||
|
|
||||||
/// Stable tagged releases.
|
|
||||||
stable,
|
|
||||||
};
|
|
||||||
|
Reference in New Issue
Block a user