1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
const std = @import("std");
const assert = std.debug.assert;
const builtin = @import("builtin");
const buildpkg = @import("src/build/main.zig");
comptime {
buildpkg.requireZig("0.14.0");
}
pub fn build(b: *std.Build) !void {
// This defines all the available build options (e.g. `-D`).
const config = try buildpkg.Config.init(b);
const test_filter = b.option(
[]const u8,
"test-filter",
"Filter for test. Only applies to Zig tests.",
);
// All our steps which we'll hook up later. The steps are shown
// up here just so that they are more self-documenting.
const run_step = b.step("run", "Run the app");
const test_step = b.step("test", "Run all tests");
const translations_step = b.step(
"update-translations",
"Update translation files",
);
// Ghostty resources like terminfo, shell integration, themes, etc.
const resources = try buildpkg.GhosttyResources.init(b, &config);
const i18n = try buildpkg.GhosttyI18n.init(b, &config);
// Ghostty dependencies used by many artifacts.
const deps = try buildpkg.SharedDeps.init(b, &config);
if (config.emit_helpgen) deps.help_strings.install();
// Ghostty executable, the actual runnable Ghostty program.
const exe = try buildpkg.GhosttyExe.init(b, &config, &deps);
// Ghostty docs
const docs = try buildpkg.GhosttyDocs.init(b, &deps);
if (config.emit_docs) {
docs.install();
} else if (config.target.result.os.tag.isDarwin()) {
// If we aren't emitting docs we need to emit a placeholder so
// our macOS xcodeproject builds since it expects the `share/man`
// directory to exist to copy into the app bundle.
docs.installDummy(b.getInstallStep());
}
// Ghostty webdata
const webdata = try buildpkg.GhosttyWebdata.init(b, &deps);
if (config.emit_webdata) webdata.install();
// Ghostty bench tools
const bench = try buildpkg.GhosttyBench.init(b, &deps);
if (config.emit_bench) bench.install();
// Ghostty dist tarball
const dist = try buildpkg.GhosttyDist.init(b, &config);
{
const step = b.step("dist", "Build the dist tarball");
step.dependOn(dist.install_step);
const check_step = b.step("distcheck", "Install and validate the dist tarball");
check_step.dependOn(dist.check_step);
check_step.dependOn(dist.install_step);
}
// libghostty
const libghostty_shared = try buildpkg.GhosttyLib.initShared(
b,
&deps,
);
const libghostty_static = try buildpkg.GhosttyLib.initStatic(
b,
&deps,
);
// Runtime "none" is libghostty, anything else is an executable.
if (config.app_runtime != .none) {
exe.install();
resources.install();
i18n.install();
} else {
// Libghostty
//
// Note: libghostty is not stable for general purpose use. It is used
// heavily by Ghostty on macOS but it isn't built to be reusable yet.
// As such, these build steps are lacking. For example, the Darwin
// build only produces an xcframework.
// We shouldn't have this guard but we don't currently
// build on macOS this way ironically so we need to fix that.
if (!config.target.result.os.tag.isDarwin()) {
libghostty_shared.installHeader(); // Only need one header
libghostty_shared.install("libghostty.so");
libghostty_static.install("libghostty.a");
}
}
// macOS only artifacts. These will error if they're initialized for
// other targets.
if (config.target.result.os.tag.isDarwin()) {
// Ghostty xcframework
const xcframework = try buildpkg.GhosttyXCFramework.init(
b,
&deps,
config.xcframework_target,
);
if (config.emit_xcframework) {
xcframework.install();
// The xcframework build always installs resources because our
// macOS xcode project contains references to them.
resources.install();
i18n.install();
}
// Ghostty macOS app
const macos_app = try buildpkg.GhosttyXcodebuild.init(
b,
&config,
.{
.xcframework = &xcframework,
.docs = &docs,
.i18n = &i18n,
.resources = &resources,
},
);
if (config.emit_macos_app) {
macos_app.install();
}
}
// Run step
run: {
if (config.app_runtime != .none) {
const run_cmd = b.addRunArtifact(exe.exe);
if (b.args) |args| run_cmd.addArgs(args);
// Set the proper resources dir so things like shell integration
// work correctly. If we're running `zig build run` in Ghostty,
// this also ensures it overwrites the release one with our debug
// build.
run_cmd.setEnvironmentVariable(
"GHOSTTY_RESOURCES_DIR",
b.getInstallPath(.prefix, "share/ghostty"),
);
run_step.dependOn(&run_cmd.step);
break :run;
}
assert(config.app_runtime == .none);
// On macOS we can run the macOS app. For "run" we always force
// a native-only build so that we can run as quickly as possible.
if (config.target.result.os.tag.isDarwin()) {
const xcframework_native = try buildpkg.GhosttyXCFramework.init(
b,
&deps,
.native,
);
const macos_app_native_only = try buildpkg.GhosttyXcodebuild.init(
b,
&config,
.{
.xcframework = &xcframework_native,
.docs = &docs,
.i18n = &i18n,
.resources = &resources,
},
);
// Run uses the native macOS app
run_step.dependOn(&macos_app_native_only.open.step);
// If we have no test filters, install the tests too
if (test_filter == null) {
macos_app_native_only.addTestStepDependencies(test_step);
}
}
}
// Tests
{
const test_exe = b.addTest(.{
.name = "ghostty-test",
.filters = if (test_filter) |v| &.{v} else &.{},
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = config.target,
.optimize = .Debug,
.strip = false,
.omit_frame_pointer = false,
.unwind_tables = .sync,
}),
});
if (config.emit_test_exe) b.installArtifact(test_exe);
_ = try deps.add(test_exe);
const test_run = b.addRunArtifact(test_exe);
test_step.dependOn(&test_run.step);
}
// update-translations does what it sounds like and updates the "pot"
// files. These should be committed to the repo.
translations_step.dependOn(i18n.update_step);
}
|