Repository navigation
Expand file tree
/
Copy pathbuild.zig
More file actions
452 lines (428 loc) · 26.8 KB
/
Copy pathbuild.zig
File metadata and controls
452 lines (428 loc) · 26.8 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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
const std = @import("std");
const Translator = @import("translate_c").Translator;
const rc = @import("rc");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// The releases leave the debug information out of the game, which on Linux the executable
// would otherwise carry, several times the size of its code.
const strip = b.option(bool, "strip", "Leave the debug information out of the game and sltool") orelse false;
// The C libraries' headers become Zig modules through the translate-c package, which the build
// compiles for the host first.
const translate_c = b.dependency("translate_c", .{});
// The library: readers for the game's files and the port of the game itself, shared by the
// game and every tool.
const lib = b.addModule("openreliant", .{
.root_source_file = b.path("src/root.zig"),
.target = target,
});
// The outline font OpenReliant carries built in, Newtown, which deps/newtown describes.
lib.addAnonymousImport("Newtown.ttf", .{ .root_source_file = b.path("deps/newtown/Newtown.ttf") });
// The definitions and the reference page of mission source as generated, which their tests
// check against.
lib.addAnonymousImport("mission.d.luau", .{ .root_source_file = b.path("docs/guide/mission.d.luau") });
lib.addAnonymousImport("mission-reference.md", .{ .root_source_file = b.path("docs/guide/mission-reference.md") });
addZlib(b, translate_c, lib, target);
// The game: SDL3 in place of Win32 and DirectX, from the SDL package, which builds SDL from
// source for the target.
// Building for a Mac other than the host, SDL and the game need the SDK's paths: from xcrun.
const macos_sdk: ?[]const u8 = if (target.result.os.tag == .macos and !target.query.isNative())
std.mem.trimEnd(u8, b.run(&.{ "xcrun", "--sdk", "macosx", "--show-sdk-path" }), "\n")
else
null;
const sdl_dependency = if (macos_sdk) |sdk| b.dependency("sdl", .{
.target = target,
.optimize = optimize,
.system_include_path = b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "usr/include" })),
.system_framework_path = b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "System/Library/Frameworks" })),
.library_path = b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "usr/lib" })),
}) else b.dependency("sdl", .{ .target = target, .optimize = optimize });
const sdl_library = sdl_dependency.artifact("SDL3");
const platform = b.createModule(.{
.root_source_file = b.path("src/platform.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "openreliant", .module = lib },
.{ .name = "sdl", .module = translateHeader(translate_c, b.path("src/platform/sdl.h"), sdl_library, target, optimize) },
},
});
// Mod post effects compile through glslang and SPIRV-Cross. Keep their C++ exceptions
// inside the platform wrapper, and ship the upstream notices with the executable. They are
// built for size: built for speed, the compiler takes gigabytes of memory on glslang's SPIR-V
// tables (`SPIRV/doc.cpp`), enough to stop a 16 GB Linux runner, and the shaders compile once
// into the shader cache anyway.
const shader_dependency = b.dependency("shader_compiler", .{ .target = target, .optimize = .small });
const shader_library = shader_dependency.artifact("shader-compiler");
platform.linkLibrary(shader_library);
platform.addIncludePath(shader_library.getEmittedIncludeTree());
platform.addCSourceFile(.{ .file = b.path("src/platform/shader_compiler.cpp"), .flags = &.{ "-std=c++17", "-fno-sanitize=undefined" } });
// The pinned versions of the libraries, which the shader cache's key covers.
platform.addAnonymousImport("shader-compiler.zon", .{ .root_source_file = b.path("deps/shader-compiler/build.zig.zon") });
for ([_][]const u8{ "LICENSE-glslang.txt", "LICENSE-spirv-cross.txt" }) |notice| {
b.getInstallStep().dependOn(&b.addInstallFile(shader_dependency.namedLazyPath(notice), notice).step);
}
// Mods' pictures are compressed for the GPU with bc7enc and rgbcx, built optimized whatever the
// game's mode, as compressing a large picture is slow otherwise.
const texture_dependency = b.dependency("texture_compressor", .{ .target = target, .optimize = .fast });
const texture_library = texture_dependency.artifact("texture-compressor");
platform.linkLibrary(texture_library);
platform.addIncludePath(texture_library.getEmittedIncludeTree());
platform.addCSourceFile(.{ .file = b.path("src/platform/texture_compressor.cpp"), .flags = &.{ "-std=c++17", "-fno-sanitize=undefined" } });
b.getInstallStep().dependOn(&b.addInstallFile(texture_dependency.namedLazyPath("LICENSE-bc7enc.txt"), "LICENSE-bc7enc.txt").step);
// The sound: OpenAL Soft in place of Miles's 3D providers, which deps/openal-soft builds from
// source for the target and the platform renders through its loopback device. It is built
// optimized whatever the game's own mode: its mixer runs in the audio device's callback and has
// to keep up with it, and unoptimized, HRTF over a burst of gunfire's voices falls behind and
// the sound stutters.
const openal_library = b.dependency("openal_soft", .{ .target = target, .optimize = .fast }).artifact("openal");
platform.addImport("al", translateHeader(translate_c, b.path("src/platform/openal.h"), openal_library, target, optimize));
// The movies: FFmpeg's Bink decoders in place of RAD's Bink library, which deps/ffmpeg builds
// from source for the target. It is built optimized whatever mode the game is built in, as
// OpenAL Soft is, so that a movie decodes in time in a debug build too.
const ffmpeg_library = b.dependency("ffmpeg", .{ .target = target, .optimize = .fast }).artifact("avcodec");
platform.addImport("av", translateHeader(translate_c, b.path("src/platform/ffmpeg.h"), ffmpeg_library, target, optimize));
// The outline fonts, which draw the interface's text at the window's resolution: FreeType,
// which deps/freetype builds from source for the target. It is built optimized whatever mode
// the game is built in, as FFmpeg is, so that a font's glyphs are drawn in time in a debug
// build too.
const freetype_library = b.dependency("freetype", .{ .target = target, .optimize = .fast }).artifact("freetype");
platform.addImport("ft", translateHeader(translate_c, b.path("src/platform/freetype.h"), freetype_library, target, optimize));
if (macos_sdk) |sdk| {
// OpenAL Soft reads its configuration through CoreFoundation on a Mac.
addMacosSdk(b, openal_library.root_module, sdk);
addMacosSdk(b, platform, sdk);
}
// Mod scripts: OpenReliant's scripting module (`src/scripting.zig`) on Luau, which deps/luau
// builds from source for the target. Like FreeType, Luau is always built optimized so that
// scripts run fast in debug builds too. The game links all of it, and sltool and the build's
// `build-mission` only its parser (`sourceParser`); the library the tools share doesn't link it.
const luau_library = b.dependency("luau", .{ .target = target, .optimize = .fast }).artifact("luau");
const scripting = b.createModule(.{
.root_source_file = b.path("src/scripting.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "openreliant", .module = lib },
.{ .name = "luau", .module = translateHeader(translate_c, b.path("src/scripting/luau.h"), luau_library, target, optimize) },
},
});
// The scripting API's definitions and reference page as generated, which its tests check
// against.
scripting.addAnonymousImport("openreliant.d.luau", .{ .root_source_file = b.path("docs/guide/openreliant.d.luau") });
scripting.addAnonymousImport("reference.md", .{ .root_source_file = b.path("docs/guide/reference.md") });
// The example mods' files, which the tests run as they ship, and the list of their scripts,
// which a test compiles one by one.
const examples = exampleFiles(b);
for (examples) |file| {
scripting.addAnonymousImport(file, .{ .root_source_file = b.path(b.fmt("examples/mods/{s}", .{file})) });
}
const example_scripts = b.addOptions();
var scripts: std.ArrayList([]const u8) = .empty;
for (examples) |file| if (std.mem.endsWith(u8, file, ".luau")) scripts.append(b.allocator, file) catch @panic("out of memory");
example_scripts.addOption([]const []const u8, "scripts", scripts.items);
scripting.addOptions("example_scripts", example_scripts);
// The parser is exported next to the library, for projects that build missions from their
// source as sltool does, such as the editor.
const source_parser = sourceParser(b, translate_c, lib, target, optimize, "source_parser");
// The definitions files, which its tests parse as luau-lsp reads them, and the guide to writing
// missions, whose missions its tests build and play.
source_parser.addAnonymousImport("openreliant.d.luau", .{ .root_source_file = b.path("docs/guide/openreliant.d.luau") });
source_parser.addAnonymousImport("mission.d.luau", .{ .root_source_file = b.path("docs/guide/mission.d.luau") });
source_parser.addAnonymousImport("missions.md", .{ .root_source_file = b.path("docs/guide/missions.md") });
// The installer unpacks the game's cabinet with libarchive, which deps/libarchive builds from
// source for the target.
const archive_dependency = b.dependency("libarchive", .{ .target = target, .optimize = optimize });
const archive_library = archive_dependency.artifact("archive");
b.getInstallStep().dependOn(&b.addInstallFile(archive_dependency.namedLazyPath("LICENSE-libarchive.txt"), "LICENSE-libarchive.txt").step);
if (macos_sdk) |sdk| addMacosSdk(b, archive_library.root_module, sdk);
// The version Release Please keeps in build.zig.zon, and where the checkout is past its last
// release, for `openreliant --version` and `sltool --version` (`src/version.zig`).
const build_options = b.addOptions();
build_options.addOption([]const u8, "version", @import("build.zig.zon").version);
build_options.addOption([]const u8, "describe", describe(b));
const version = b.createModule(.{
.root_source_file = b.path("src/version.zig"),
.target = target,
.optimize = optimize,
});
version.addOptions("build_options", build_options);
const openreliant = b.addExecutable(.{
.name = "openreliant",
.root_module = b.createModule(.{
.root_source_file = b.path("src/openreliant/main.zig"),
.target = target,
.optimize = optimize,
.strip = strip,
.imports = &.{
.{ .name = "openreliant", .module = lib },
.{ .name = "platform", .module = platform },
.{ .name = "scripting", .module = scripting },
.{ .name = "archive", .module = translateHeader(translate_c, b.path("src/openreliant/archive.h"), archive_library, target, optimize) },
.{ .name = "version", .module = version },
},
}),
});
b.installArtifact(openreliant);
// Mission 0, OpenReliant's own: the sandbox as a standard mission file, which the build compiles
// from its source (`src/openreliant/mission0.mission.luau`) with the mission source compiler,
// built for the host (`src/tools/build_mission.zig`). The game plays it as its default mission,
// from the copy it carries, and the build installs it too, for `sltool` and the original.
const host_lib = b.createModule(.{
.root_source_file = b.path("src/root.zig"),
.target = b.graph.host,
});
addZlib(b, translate_c, host_lib, b.graph.host);
const build_mission = b.addExecutable(.{
.name = "build-mission",
.root_module = b.createModule(.{
.root_source_file = b.path("src/tools/build_mission.zig"),
.target = b.graph.host,
.imports = &.{
.{ .name = "openreliant", .module = host_lib },
.{ .name = "source_parser", .module = sourceParser(b, translate_c, host_lib, b.graph.host, .debug, null) },
},
}),
});
const compile_mission0 = b.addRunArtifact(build_mission);
compile_mission0.addFileArg(b.path("src/openreliant/mission0.mission.luau"));
const mission0_file = compile_mission0.addOutputFileArg2("mission0.dte", .{});
openreliant.root_module.addAnonymousImport("mission0.dte", .{ .root_source_file = mission0_file });
b.getInstallStep().dependOn(&b.addInstallFile(mission0_file, "missions/mission0.dte").step);
// The icon the window shows where the system takes it from the window rather than from the
// executable (`src/openreliant/icon.zig`).
for ([_][]const u8{ "openreliant-256.png", "openreliant-512.png" }) |icon| {
openreliant.root_module.addAnonymousImport(icon, .{ .root_source_file = b.path(b.fmt("assets/icon/{s}", .{icon})) });
}
runStep(b, "play", "Run the game", openreliant);
const sltool = b.addExecutable(.{
.name = "sltool",
.root_module = b.createModule(.{
.root_source_file = b.path("src/tools/sltool/main.zig"),
.target = target,
.optimize = optimize,
.strip = strip,
.imports = &.{
.{ .name = "openreliant", .module = lib },
.{ .name = "source_parser", .module = source_parser },
.{ .name = "version", .module = version },
},
}),
});
b.installArtifact(sltool);
// `zig build sltool` installs sltool alone, without the game and the libraries only it needs,
// as the mods' release pipeline does to pack mods.
const sltool_step = b.step("sltool", "Build and install sltool alone");
sltool_step.dependOn(&b.addInstallArtifact(sltool, .{}).step);
// On Windows, both programs carry the icon, version information and a manifest
// (`src/windows.manifest`). `build-resources`, built for the host, writes the script that names
// the icon and gives the version (`src/tools/build_resources.zig`).
const host_version = b.createModule(.{
.root_source_file = b.path("src/version.zig"),
.target = b.graph.host,
});
host_version.addOptions("build_options", build_options);
const build_resources = b.addExecutable(.{
.name = "build-resources",
.root_module = b.createModule(.{
.root_source_file = b.path("src/tools/build_resources.zig"),
.target = b.graph.host,
.imports = &.{
.{ .name = "version", .module = host_version },
},
}),
});
if (target.result.os.tag == .windows) {
for ([_]*std.Build.Step.Compile{ openreliant, sltool }) |program| addWindowsResources(b, program, build_resources, target, optimize);
}
// Derives the engine's static tables from the game binary: the script VM's opcodes, commands
// and conditions, and the models it loads. Not installed: it is a development tool, run by the
// `make vm-*` and `make model-tables` targets, and its output is committed.
const tablegen = b.addExecutable(.{
.name = "tablegen",
.root_module = b.createModule(.{
.root_source_file = b.path("src/tools/tablegen/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "openreliant", .module = lib },
},
}),
});
// Its tests check that each emitter writes the table committed from it again, byte for byte
// once formatted (`testing.expectCommitted`).
for ([_][2][]const u8{
.{ "committed commands", "src/engine/game/executor/commands.zig" },
.{ "committed conditions", "src/engine/vm/conditions.zig" },
.{ "committed flight", "src/engine/game/create/flight.zig" },
.{ "committed combat", "src/engine/game/create/combat.zig" },
.{ "committed guns", "src/engine/game/guns/stats.zig" },
.{ "committed views", "src/engine/game/camera/views.zig" },
.{ "committed objectives", "src/engine/game/hud/objectives.zig" },
.{ "committed sounds", "src/engine/game/sound3d/sounds.zig" },
.{ "committed orders", "src/engine/game/ai/orders.zig" },
.{ "committed faces", "src/engine/game/pilots/faces.zig" },
.{ "committed pool", "src/engine/game/pilots/pool.zig" },
.{ "committed rooms", "src/engine/game/interface/rooms/views.zig" },
.{ "committed locker", "src/engine/game/interface/locker/tables.zig" },
.{ "committed speech", "src/engine/game/voice/tables.zig" },
.{ "committed sequences", "src/engine/game/explode/sequences.zig" },
.{ "committed controls", "src/engine/input/controls.zig" },
.{ "committed models", "src/engine/game/create/models.zig" },
.{ "committed maneuvers", "src/engine/game/aidefend/maneuvers.zig" },
.{ "committed itac", "src/engine/game/itac/tables.zig" },
.{ "committed sources", "src/engine/sources.zig" },
.{ "committed opcodes", "src/engine/vm/opcodes.zig" },
}) |table| {
tablegen.root_module.addAnonymousImport(table[0], .{ .root_source_file = b.path(table[1]) });
}
const tablegen_step = b.step("tablegen", "Build the generator of the engine's tables");
tablegen_step.dependOn(&b.addInstallArtifact(tablegen, .{}).step);
// Writes the names and data types the Ghidra scripts apply, from the Zig definitions. Not
// installed either: `make ghidra-annotate` runs it.
const ghidragen = b.addExecutable(.{
.name = "ghidragen",
.root_module = b.createModule(.{
.root_source_file = b.path("src/tools/ghidragen/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "openreliant", .module = lib },
},
}),
});
// Its tests check the names tables kept by hand, which Ghidra applies with its own rows.
for ([_][]const u8{ "LANCER.EXE.tsv", "LANCER.EXE.runtime.tsv", "srd3d.dll.tsv" }) |table| {
ghidragen.root_module.addAnonymousImport(table, .{
.root_source_file = b.path(b.fmt("ghidra/names/{s}", .{table})),
});
}
const ghidragen_step = b.step("ghidragen", "Build the Ghidra name and type table generator");
ghidragen_step.dependOn(&b.addInstallArtifact(ghidragen, .{}).step);
runStep(b, "run", "Run sltool", sltool);
// Every module's tests, and `zig build check`, which compiles the programs and the tests
// without linking them (the C libraries, translate-c and mission 0 build once first):
// `zig build check -fincremental --watch` reports compile errors within moments of a save.
const test_step = b.step("test", "Run tests");
const check_step = b.step("check", "Check that the programs and the tests compile, without writing binaries");
for ([_]*std.Build.Module{ lib, platform, scripting, source_parser, version, openreliant.root_module, sltool.root_module, tablegen.root_module, ghidragen.root_module, build_resources.root_module }) |module| {
const tests = b.addTest(.{ .root_module = module });
test_step.dependOn(&b.addRunArtifact(tests).step);
check_step.dependOn(&b.addTest(.{ .root_module = module }).step);
}
for ([_]*std.Build.Step.Compile{ openreliant, sltool, tablegen, ghidragen, build_resources }) |program| {
check_step.dependOn(&b.addExecutable(.{ .name = program.name, .root_module = program.root_module }).step);
}
}
/// A step `name` that installs everything, then runs `program` with the arguments given after
/// `--`.
fn runStep(b: *std.Build, name: []const u8, description: []const u8, program: *std.Build.Step.Compile) void {
const run = b.addRunArtifact(program);
run.step.dependOn(b.getInstallStep());
run.addPassthruArgs();
b.step(name, description).dependOn(&run.step);
}
/// Gives `program`, built for Windows, the resources `build_resources` writes the script of, the
/// icon and the version information, and the manifest, `src/windows.manifest`. The rc package
/// compiles them, finding the icon in `assets/icon`, without the system's headers, so every system
/// builds the same bytes. Its resource compiler only reads these few files, so it is built in Debug,
/// which compiles fastest, whatever mode the rest is built in.
fn addWindowsResources(b: *std.Build, program: *std.Build.Step.Compile, build_resources: *std.Build.Step.Compile, target: std.Build.ResolvedTarget, optimize: std.lang.Optimize) void {
const run = b.addRunArtifact(build_resources);
run.addArg(program.name);
const script = run.addOutputFileArg2(b.fmt("{s}-resources.rc", .{program.name}), .{});
const resources: rc.Compiler = .init(b.dependency("rc", .{ .optimize = .debug }), .{
.rc_files = &.{script},
.manifest = .{ .file = b.path("src/windows.manifest"), .for_dll = false },
.target = target,
.optimize = optimize,
.rc_options = .{ .auto_includes = .none, .include_paths = &.{b.path("assets/icon")} },
});
program.root_module.addObjectFile(resources.getObjectFile());
}
/// The module that reads mission source with Luau's own parser (`src/source_parser.zig`), for
/// `target`, on `lib`, the library built for it. It links the parser through a C++ shim, since the
/// parser has no C API. With a `name`, other packages can import the module by that name.
fn sourceParser(b: *std.Build, translate_c: *std.Build.Dependency, lib: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.lang.Optimize, name: ?[]const u8) *std.Build.Module {
const luau_ast_library = b.dependency("luau", .{ .target = target, .optimize = .fast }).artifact("luau-ast");
const options: std.Build.Module.CreateOptions = .{
.root_source_file = b.path("src/source_parser.zig"),
.target = target,
.optimize = optimize,
.link_libcpp = true,
.imports = &.{
.{ .name = "openreliant", .module = lib },
.{ .name = "luau_syntax", .module = translateHeader(translate_c, b.path("src/source_parser/luau.h"), luau_ast_library, target, optimize) },
},
};
const module = if (name) |exported| b.addModule(exported, options) else b.createModule(options);
module.linkLibrary(luau_ast_library);
module.addIncludePath(luau_ast_library.getEmittedIncludeTree());
module.addCSourceFile(.{ .file = b.path("src/source_parser/luau.cpp"), .flags = &.{ "-std=c++17", "-fno-sanitize=undefined" } });
return module;
}
/// The Zig module translate-c makes of `header`, which includes `library`'s headers. The module
/// links `library`, so a module that imports it links the library too. Its structs' fields
/// default to zero, as C's do in a partial initializer, so that the platform code can name only
/// the fields it sets.
fn translateHeader(translate_c: *std.Build.Dependency, header: std.Build.LazyPath, library: *std.Build.Step.Compile, target: std.Build.ResolvedTarget, optimize: std.lang.Optimize) *std.Build.Module {
const translator: Translator = .init(translate_c, .{ .c_source_file = header, .target = target, .optimize = optimize, .default_init = true });
translator.linkLibrary(library);
return translator.mod;
}
/// Gives `module`, the library built from `src/root.zig`, zlib for `target`, which the PNG reader
/// inflates with (`src/formats/png.zig`): several times faster than `std.compress.flate`. It is
/// built optimized whatever mode the rest is built in, as the other C libraries are. libarchive asks
/// for zlib with the same options, so the game builds and links one copy.
fn addZlib(b: *std.Build, translate_c: *std.Build.Dependency, module: *std.Build.Module, target: std.Build.ResolvedTarget) void {
const zlib = b.dependency("zlib", .{ .target = target, .optimize = .fast }).artifact("z");
module.addImport("zlib", translateHeader(translate_c, zlib.getEmittedIncludeTree().path(b, "zlib.h"), zlib, target, .fast));
}
/// Gives `module` the macOS SDK's headers, frameworks and libraries at `sdk`, which a build for a
/// Mac other than the host does not find by itself.
fn addMacosSdk(b: *std.Build, module: *std.Build.Module, sdk: []const u8) void {
module.addSystemIncludePath(b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "usr/include" })));
module.addSystemFrameworkPath(b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "System/Library/Frameworks" })));
module.addLibraryPath(b.graph.cwdRelativePath(b.pathJoin(&.{ sdk, "usr/lib" })));
}
/// `git describe` of the checkout against the release tags, such as `v0.2.0-12-gabc1234-dirty`, or
/// nothing where there is no git or no tag, as in a source archive. The build can't track what
/// the answer depends on (new commits, edits to any file), so asking poisons the configuration
/// cache: the build runs build.zig again each time, as it always did before Zig 0.17.
fn describe(b: *std.Build) []const u8 {
b.graph.poisonCache();
const root = b.root.toString(b.allocator) catch @panic("out of memory");
return switch (b.runFallible(&.{ "git", "-C", root, "describe", "--tags", "--match", "v*", "--long", "--dirty", "--abbrev=7" }, .{ .stderr_behavior = .ignore })) {
.success => |out| std.mem.trimEnd(u8, out, "\n"),
else => "",
};
}
/// Every file of the example mods, as `<mod>/<file>`, sorted: a mod's files are directly in its
/// folder.
fn exampleFiles(b: *std.Build) []const []const u8 {
const io = b.graph.io;
// The listing runs while build.zig configures, so the build is told to configure again when
// a mod or a file is added, removed or renamed.
b.dependOnDirectoryContents(b.path("examples/mods"));
var mods = b.root.root_dir.handle.openDir(io, b.pathJoin(&.{ b.root.sub_path, "examples/mods" }), .{ .iterate = true }) catch |err| std.debug.panic("can't open examples/mods: {t}", .{err});
defer mods.close(io);
var found: std.ArrayList([]const u8) = .empty;
var each_mod = mods.iterate();
while (each_mod.next(io) catch |err| std.debug.panic("can't list examples/mods: {t}", .{err})) |mod| {
if (mod.kind != .directory) continue;
var folder = mods.openDir(io, mod.name, .{ .iterate = true }) catch |err| std.debug.panic("can't open examples/mods/{s}: {t}", .{ mod.name, err });
defer folder.close(io);
b.dependOnDirectoryContents(b.path(b.fmt("examples/mods/{s}", .{mod.name})));
var each_file = folder.iterate();
while (each_file.next(io) catch |err| std.debug.panic("can't list examples/mods/{s}: {t}", .{ mod.name, err })) |file| {
if (file.kind != .file) continue;
found.append(b.allocator, b.fmt("{s}/{s}", .{ mod.name, file.name })) catch @panic("out of memory");
}
}
std.mem.sort([]const u8, found.items, {}, struct {
fn less(_: void, left: []const u8, right: []const u8) bool {
return std.mem.lessThan(u8, left, right);
}
}.less);
return found.items;
}