Repository navigation
Expand file tree
/
Copy pathbuild.zig
More file actions
296 lines (266 loc) · 13.1 KB
/
Copy pathbuild.zig
File metadata and controls
296 lines (266 loc) · 13.1 KB
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{
.default_target = if (@import("builtin").os.tag == .macos)
.{
// Release binaries must not depend on the build machine's CPU
// extensions, especially on virtualized Intel macOS runners.
.cpu_model = .baseline,
.os_version_min = .{ .semver = .{ .major = 14, .minor = 0, .patch = 0 } },
}
else
.{},
});
const optimize = b.standardOptimizeOption(.{});
// Optional sysroot for cross-compilation (-Dsysroot=/path/to/sysroot).
// Linux: <sysroot>/include and <sysroot>/lib; macOS: an Apple SDK.
// Needed because b.addTranslateC does not inherit --search-prefix.
const sysroot_opt = b.option([]const u8, "sysroot", "Sysroot for cross-compile translate-c include/library paths") orelse
if (target.result.os.tag == .macos and @import("builtin").os.tag == .macos)
std.mem.trim(u8, b.run(&.{ "xcrun", "--sdk", "macosx", "--show-sdk-path" }), " \r\n\t")
else
null;
const sysroot_inc: ?std.Build.LazyPath = if (sysroot_opt) |s|
.{ .cwd_relative = b.pathJoin(&.{ s, if (target.result.os.tag == .macos) "usr/include" else "include" }) }
else
null;
const sysroot_lib: ?std.Build.LazyPath = if (sysroot_opt) |s|
.{ .cwd_relative = b.pathJoin(&.{ s, if (target.result.os.tag == .macos) "usr/lib" else "lib" }) }
else
null;
const backend_opt = b.option([]const u8, "backend", "Rendering backend: fbdev, x11, wayland, or macos") orelse if (target.result.os.tag == .macos) "macos" else "fbdev";
const is_x11 = std.mem.eql(u8, backend_opt, "x11");
const is_wayland = std.mem.eql(u8, backend_opt, "wayland");
const is_macos = std.mem.eql(u8, backend_opt, "macos");
if (!is_x11 and !is_wayland and !is_macos and !std.mem.eql(u8, backend_opt, "fbdev")) {
std.debug.panic("invalid -Dbackend='{s}'; expected fbdev, x11, wayland, or macos", .{backend_opt});
}
if (is_macos != (target.result.os.tag == .macos)) {
@panic("macOS targets require -Dbackend=macos; Linux backends require a Linux target");
}
if (!is_macos and target.result.os.tag != .linux) {
@panic("zt supports Linux and macOS targets");
}
const keymap_opt = b.option([]const u8, "keymap", "Keyboard layout: us or jp (default: us)") orelse "us";
const use_jp_keymap = std.mem.eql(u8, keymap_opt, "jp");
if (!use_jp_keymap and !std.mem.eql(u8, keymap_opt, "us")) {
std.debug.panic("invalid -Dkeymap='{s}'; expected us or jp", .{keymap_opt});
}
// EXPERIMENTAL: embed a custom font instead of the default blob.
// The font is converted to zt's binary blob format at build time by the
// pure-Zig host tools (no external dependencies):
// - .bdf -> tools/bdf2blob.zig
// - .ttf -> tools/ttf2bdf.zig (rasterize outlines) -> bdf2blob
// Without -Dfont, the committed src/fonts/ufo-nf.bin is used.
const font_opt = b.option([]const u8, "font", "Path to a custom BDF or TTF font to embed instead of the default (EXPERIMENTAL)");
const font_blob: std.Build.LazyPath = if (font_opt) |font_path| blk: {
const is_ttf = std.mem.endsWith(u8, font_path, ".ttf");
if (!is_ttf and !std.mem.endsWith(u8, font_path, ".bdf")) {
std.debug.panic("-Dfont: unsupported font '{s}'; expected .bdf or .ttf (OTF/CFF is not supported)", .{font_path});
}
// .ttf -> intermediate .bdf via the rasterizer.
const bdf_path: std.Build.LazyPath = if (is_ttf) tt: {
const ttf2bdf = hostTool(b, "ttf2bdf");
const run = b.addRunArtifact(ttf2bdf);
run.stdio = .inherit;
run.addFileArg(.{ .cwd_relative = font_path });
break :tt run.addOutputFileArg("font.bdf");
} else .{ .cwd_relative = font_path };
// .bdf -> binary blob.
const bdf2blob = hostTool(b, "bdf2blob");
const run = b.addRunArtifact(bdf2blob);
run.stdio = .inherit;
run.addFileArg(bdf_path);
break :blk run.addOutputFileArg("ufo-nf.bin");
} else b.path("src/fonts/ufo-nf.bin");
const scale_opt = b.option(u32, "scale", "Pixel scale factor: 1, 2, or 4 (default: 1)") orelse 1;
const max_fps_opt = b.option(u32, "max_fps", "Maximum frame rate: 0 = unlimited (default: 120)") orelse 120;
const pty_buf_kb_opt = b.option(u32, "pty_buf_kb", "PTY read buffer size in KB (default: 1024)") orelse 1024;
if (pty_buf_kb_opt == 0) {
@panic("invalid -Dpty_buf_kb=0; expected a positive buffer size");
}
const scrollback_lines_opt = b.option(u32, "scrollback_lines", "Scrollback rows for main screen (0 disables, default 10000)") orelse 10000;
const alt_screen_wheel_scrollback_opt = b.option(bool, "alt_screen_wheel_scrollback", "Make the mouse wheel scroll the alt-screen scrollback ring instead of arrow keys (default: false)") orelse false;
const shell_opt_raw = b.option([]const u8, "shell", "Shell path (default: /bin/zsh on macOS, /bin/sh on Linux)") orelse if (is_macos) "/bin/zsh" else "/bin/sh";
const shell_opt: [:0]const u8 = b.allocator.dupeSentinel(u8, shell_opt_raw, 0) catch @panic("OOM");
const options = b.addOptions();
options.addOption(bool, "use_x11", is_x11);
options.addOption(bool, "use_wayland", is_wayland);
options.addOption(bool, "use_macos", is_macos);
options.addOption(bool, "macos_gui_tests", b.option(bool, "macos_gui_tests", "Run Cocoa integration tests (requires a macOS desktop session)") orelse false);
options.addOption(bool, "use_jp_keymap", use_jp_keymap);
options.addOption(u32, "scale", scale_opt);
options.addOption(u32, "max_fps", max_fps_opt);
options.addOption(u32, "pty_buf_kb", pty_buf_kb_opt);
options.addOption(u32, "scrollback_lines", scrollback_lines_opt);
options.addOption(bool, "alt_screen_wheel_scrollback", alt_screen_wheel_scrollback_opt);
options.addOption([:0]const u8, "shell", shell_opt);
options.addOption([:0]const u8, "version", b.allocator.dupeSentinel(u8, "0.11.0", 0) catch @panic("OOM"));
const config_mod = b.createModule(.{
.root_source_file = b.path("config.zig"),
.imports = &.{
.{ .name = "build_options", .module = options.createModule() },
},
});
const strip_opt = b.option(bool, "strip", "Strip debug info and symbols (default: false)") orelse false;
const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.strip = if (strip_opt) true else null,
.imports = &.{
.{ .name = "config", .module = config_mod },
},
});
exe_mod.addAnonymousImport("font_blob", .{ .root_source_file = font_blob });
const exe = b.addExecutable(.{
.name = "zt",
.root_module = exe_mod,
});
if (is_x11) {
exe_mod.linkSystemLibrary("xcb", .{});
exe_mod.linkSystemLibrary("xcb-shm", .{});
exe_mod.linkSystemLibrary("xcb-xkb", .{});
exe_mod.linkSystemLibrary("xcb-imdkit", .{});
exe_mod.linkSystemLibrary("xcb-util", .{});
exe_mod.linkSystemLibrary("xkbcommon", .{});
exe_mod.linkSystemLibrary("xkbcommon-x11", .{});
// Allow cross-compilation against shared libs with newer glibc
exe.linker_allow_shlib_undefined = true;
exe_mod.link_libc = true;
if (sysroot_inc) |p| exe_mod.addIncludePath(p);
if (sysroot_lib) |p| exe_mod.addLibraryPath(p);
// translate-c for xcb + shm + xim + xkb (x11.zig).
// src/c_x11.h renames xcb_shm_id → _zt_xcb_shm_id_stub so translate-c
// can process xcb/shm.h without hitting the opaque-extern limitation;
// the real xcb_shm_id is declared manually in x11.zig.
const c_x11 = b.addTranslateC(.{
.root_source_file = b.path("src/c_x11.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
if (sysroot_inc) |p| c_x11.addIncludePath(p);
exe_mod.addImport("c_x11", c_x11.createModule());
// translate-c for xkbcommon (shared with wayland backend)
const c_xkb_x11 = b.addTranslateC(.{
.root_source_file = b.path("src/c_xkb.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
if (sysroot_inc) |p| c_xkb_x11.addIncludePath(p);
exe_mod.addImport("c_xkb", c_xkb_x11.createModule());
} else if (is_wayland) {
exe_mod.linkSystemLibrary("xkbcommon", .{});
exe_mod.link_libc = true;
if (sysroot_inc) |p| exe_mod.addIncludePath(p);
if (sysroot_lib) |p| exe_mod.addLibraryPath(p);
const c_xkb_wl = b.addTranslateC(.{
.root_source_file = b.path("src/c_xkb.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
if (sysroot_inc) |p| c_xkb_wl.addIncludePath(p);
exe_mod.addImport("c_xkb", c_xkb_wl.createModule());
} else if (is_macos) {
// Unsigned Intel Mach-O files need space for codesign to append its
// LC_CODE_SIGNATURE load command without overwriting the first code bytes.
exe.headerpad_size = 0x1000;
exe_mod.linkFramework("Cocoa", .{});
exe_mod.linkFramework("CoreGraphics", .{});
exe_mod.link_libc = true;
if (sysroot_opt) |s| {
exe_mod.addFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ s, "System/Library/Frameworks" }) });
exe_mod.addSystemIncludePath(sysroot_inc.?);
exe_mod.addLibraryPath(sysroot_lib.?);
}
const c_pty_macos = b.addTranslateC(.{
.root_source_file = b.path("src/c_pty_macos.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
if (sysroot_inc) |p| c_pty_macos.addIncludePath(p);
exe_mod.addImport("c_pty_macos", c_pty_macos.createModule());
}
b.installArtifact(exe);
const test_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "config", .module = config_mod },
},
});
test_mod.addAnonymousImport("font_blob", .{ .root_source_file = font_blob });
const unit_tests = b.addTest(.{
.root_module = test_mod,
});
if (is_x11) {
test_mod.linkSystemLibrary("xcb", .{});
test_mod.linkSystemLibrary("xcb-shm", .{});
test_mod.linkSystemLibrary("xcb-xkb", .{});
test_mod.linkSystemLibrary("xcb-imdkit", .{});
test_mod.linkSystemLibrary("xcb-util", .{});
test_mod.linkSystemLibrary("xkbcommon", .{});
test_mod.linkSystemLibrary("xkbcommon-x11", .{});
test_mod.link_libc = true;
const c_x11_test = b.addTranslateC(.{
.root_source_file = b.path("src/c_x11.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
test_mod.addImport("c_x11", c_x11_test.createModule());
const c_xkb_x11_test = b.addTranslateC(.{
.root_source_file = b.path("src/c_xkb.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
test_mod.addImport("c_xkb", c_xkb_x11_test.createModule());
} else if (is_wayland) {
test_mod.linkSystemLibrary("xkbcommon", .{});
test_mod.link_libc = true;
const c_xkb_wl_test = b.addTranslateC(.{
.root_source_file = b.path("src/c_xkb.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
test_mod.addImport("c_xkb", c_xkb_wl_test.createModule());
} else if (is_macos) {
test_mod.linkFramework("Cocoa", .{});
test_mod.linkFramework("CoreGraphics", .{});
test_mod.link_libc = true;
if (sysroot_opt) |s| {
test_mod.addFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ s, "System/Library/Frameworks" }) });
test_mod.addSystemIncludePath(sysroot_inc.?);
test_mod.addLibraryPath(sysroot_lib.?);
}
const c_pty_macos_test = b.addTranslateC(.{
.root_source_file = b.path("src/c_pty_macos.h"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
if (sysroot_inc) |p| c_pty_macos_test.addIncludePath(p);
test_mod.addImport("c_pty_macos", c_pty_macos_test.createModule());
}
const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
}
/// Build a pure-Zig font-conversion tool for the host (used at build time).
fn hostTool(b: *std.Build, name: []const u8) *std.Build.Step.Compile {
return b.addExecutable(.{
.name = name,
.root_module = b.createModule(.{
.root_source_file = b.path(b.fmt("tools/{s}.zig", .{name})),
.target = b.graph.host,
.optimize = .fast,
}),
});
}