-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.zig
333 lines (301 loc) · 12.4 KB
/
build.zig
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
// raylib-zig (c) Nikolas Wipper 2020-2023
const std = @import("std");
const rl = @This();
const builtin = @import("builtin");
const buildList = @import("build-list.zig");
const Program = buildList.Program;
const programs = buildList.programs;
const tests = buildList.tests;
pub fn link(
b: *std.Build,
exe: *std.Build.Step.Compile,
target: std.zig.CrossTarget,
optimize: std.builtin.Mode,
) void {
const raylib = b.dependency("raylib", .{
.target = target,
.optimize = optimize,
});
var art = raylib.artifact("raylib");
const target_os = exe.target.toTarget().os.tag;
switch (target_os) {
.windows => {
exe.linkSystemLibrary("winmm");
exe.linkSystemLibrary("gdi32");
exe.linkSystemLibrary("opengl32");
},
.macos => {
exe.linkFramework("OpenGL");
exe.linkFramework("Cocoa");
exe.linkFramework("IOKit");
exe.linkFramework("CoreAudio");
exe.linkFramework("CoreVideo");
},
.freebsd, .openbsd, .netbsd, .dragonfly => {
exe.linkSystemLibrary("GL");
exe.linkSystemLibrary("rt");
exe.linkSystemLibrary("dl");
exe.linkSystemLibrary("m");
exe.linkSystemLibrary("X11");
exe.linkSystemLibrary("Xrandr");
exe.linkSystemLibrary("Xinerama");
exe.linkSystemLibrary("Xi");
exe.linkSystemLibrary("Xxf86vm");
exe.linkSystemLibrary("Xcursor");
},
.emscripten, .wasi => {
// When using emscripten, the libries don't need to be linked
// because emscripten is going to do that later.
},
else => { // Linux and possibly others.
exe.linkSystemLibrary("GL");
exe.linkSystemLibrary("rt");
exe.linkSystemLibrary("dl");
exe.linkSystemLibrary("m");
exe.linkSystemLibrary("X11");
},
}
exe.linkLibrary(art);
}
pub fn getArtifact(
b: *std.Build,
target: std.zig.CrossTarget,
optimize: std.builtin.Mode,
) *std.Build.Step.Compile {
const raylib = b.dependency("raylib", .{
.target = target,
.optimize = optimize,
});
return raylib.artifact("raylib");
}
// TODO: Make these not comptime.
pub fn getModule(b: *std.Build, comptime rl_path: []const u8) *std.Build.Module {
if (b.modules.contains("raylib")) {
return b.modules.get("raylib").?;
}
return b.addModule("raylib", .{ .source_file = .{ .path = rl_path ++ "/lib/raylib-zig.zig" } });
}
fn getModuleInternal(b: *std.Build) *std.Build.Module {
if (b.modules.contains("raylib")) {
return b.modules.get("raylib").?;
}
return b.addModule("raylib", .{ .source_file = .{ .path = "lib/raylib-zig.zig" } });
}
pub const math = struct {
pub fn getModule(b: *std.Build, comptime rl_path: []const u8) *std.Build.Module {
var raylib = rl.getModule(b, rl_path);
return b.addModule("raylib-math", .{
.source_file = .{ .path = rl_path ++ "/lib/raylib-zig-math.zig" },
.dependencies = &.{.{ .name = "raylib-zig", .module = raylib }},
});
}
fn getModuleInternal(b: *std.Build) *std.Build.Module {
var raylib = rl.getModuleInternal(b);
return b.addModule("raylib-math", .{
.source_file = .{ .path = "lib/raylib-zig-math.zig" },
.dependencies = &.{.{ .name = "raylib-zig", .module = raylib }},
});
}
};
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
var raylib = rl.getModuleInternal(b);
var raylib_math = rl.math.getModuleInternal(b);
const raylib_group = b.addModule("raylib-group", .{
.source_file = .{ .path = "lib/raylib-group.zig" },
.dependencies = &.{.{ .name = "raylib", .module = raylib }},
});
for (programs) |prog| {
if (target.getOsTag() == .emscripten) {
const exe_lib = compileForEmscripten(b, prog.name, prog.path, target, optimize);
exe_lib.addModule("raylib", raylib);
exe_lib.addModule("raylib-math", raylib_math);
exe_lib.addModule("raylib-group", raylib_group);
const raylib_artifact = getArtifact(b, target, optimize);
// Note that raylib itself isn't actually added to the exe_lib
// output file, so it also needs to be linked with emscripten.
exe_lib.linkLibrary(raylib_artifact);
const link_step = try linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact });
link_step.addArg("--embed-file");
link_step.addArg("resources/");
const run_step = try emscriptenRunStep(b);
run_step.step.dependOn(&link_step.step);
const run_option = b.step(prog.name, prog.desc);
run_option.dependOn(&run_step.step);
} else {
const exe = b.addExecutable(.{
.name = prog.name,
.root_source_file = .{ .path = prog.path },
.optimize = optimize,
.target = target,
});
rl.link(b, exe, target, optimize);
exe.addModule("raylib", raylib);
exe.addModule("raylib-math", raylib_math);
exe.addModule("raylib-group", raylib_group);
if (optimize != .Debug) exe.subsystem = .Windows;
const install_cmd = b.addInstallArtifact(exe, .{});
b.getInstallStep().dependOn(&install_cmd.step);
const run_cmd = b.addRunArtifact(exe);
const run_step = b.step(prog.name, prog.desc);
run_step.dependOn(&install_cmd.step);
run_step.dependOn(&run_cmd.step);
}
}
for (tests) |t| {
const u_test = b.addTest(.{
.root_source_file = .{ .path = t.path },
.optimize = optimize,
.target = target,
});
rl.link(b, u_test, target, optimize);
u_test.addModule("raylib", raylib);
u_test.addModule("raylib-math", raylib_math);
const run_cmd = b.addRunArtifact(u_test);
const test_step = b.step(t.name, t.desc);
test_step.dependOn(&run_cmd.step);
}
}
const emccOutputDir = "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str;
const emccOutputFile = "index.html";
pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run {
// Find emrun.
if (b.sysroot == null) {
@panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'");
}
// If compiling on windows , use emrun.bat.
const emrunExe = switch (builtin.os.tag) {
.windows => "emrun.bat",
else => "emrun",
};
const emrun_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emrunExe.len + 1);
defer b.allocator.free(emrun_run_arg);
_ = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ b.sysroot.?, emrunExe });
const run_cmd = b.addSystemCommand(&[_][]const u8{ emrun_run_arg, emccOutputDir ++ emccOutputFile });
return run_cmd;
}
// Creates the static library to build a project for Emscripten.
pub fn compileForEmscripten(
b: *std.Build,
name: []const u8,
root_source_file: []const u8,
target: std.zig.CrossTarget,
optimize: std.builtin.Mode,
) *std.Build.Step.Compile {
// TODO: It might be a good idea to create a custom compile step, that does
// both the compile to static library and the link with emcc by overidding
// the make function of the step. However it might also be a bad idea since
// it messes with the build system itself.
const new_target = updateTargetForWeb(target);
// The project is built as a library and linked later.
const exe_lib = b.addStaticLibrary(.{
.name = name,
.root_source_file = .{ .path = root_source_file },
.target = new_target,
.optimize = optimize,
});
// There are some symbols that need to be defined in C.
const webhack_c_file_step = b.addWriteFiles();
const webhack_c_file = webhack_c_file_step.add("webhack.c", webhack_c);
exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]u8{} });
// Since it's creating a static library, the symbols raylib uses to webgl
// and glfw don't need to be linked by emscripten yet.
exe_lib.step.dependOn(&webhack_c_file_step.step);
return exe_lib;
}
// Links a set of items together using emscripten.
//
// Will accept objects and static libraries as items to link. As for files to
// include, it is recomended to have a single resources directory and just pass
// the entire directory instead of passing every file individually. The entire
// path given will be the path to read the file within the program. So, if
// "resources/image.png" is passed, your program will use "resources/image.png"
// as the path to load the file.
//
// TODO: Test if shared libraries are accepted, I don't remember if emcc can
// link a shared library with a project or not.
// TODO: Add a parameter that allows a custom output directory.
pub fn linkWithEmscripten(
b: *std.Build,
itemsToLink: []const *std.Build.Step.Compile,
) !*std.Build.Step.Run {
// Raylib uses --sysroot in order to find emscripten, so do the same here.
if (b.sysroot == null) {
@panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'");
}
const emccExe = switch (builtin.os.tag) {
.windows => "emcc.bat",
else => "emcc",
};
var emcc_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emccExe.len + 1);
defer b.allocator.free(emcc_run_arg);
emcc_run_arg = try std.fmt.bufPrint(
emcc_run_arg,
"{s}" ++ std.fs.path.sep_str ++ "{s}",
.{ b.sysroot.?, emccExe },
);
// Create the output directory because emcc can't do it.
const mkdir_command = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", emccOutputDir });
// Actually link everything together.
const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_run_arg});
for (itemsToLink) |item| {
emcc_command.addFileArg(item.getEmittedBin());
emcc_command.step.dependOn(&item.step);
}
// This puts the file in zig-out/htmlout/index.html.
emcc_command.step.dependOn(&mkdir_command.step);
emcc_command.addArgs(&[_][]const u8{
"-o",
emccOutputDir ++ emccOutputFile,
"-sFULL-ES3=1",
"-sUSE_GLFW=3",
"-sASYNCIFY",
"-O3",
"--emrun",
});
return emcc_command;
}
// TODO: See if zig's standard library already has somehing like this.
fn lastIndexOf(string: []const u8, character: u8) usize {
// Interestingly, Zig has no nice way of iterating a slice backwards.
for (0..string.len) |i| {
const index = string.len - i - 1;
if (string[index] == character) return index;
}
return string.len - 1;
}
// TODO: each zig update, remove this and see if everything still works.
// https://github.com/ziglang/zig/issues/16776 is where the issue is submitted.
fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget {
// Zig building to emscripten doesn't work, because the Zig standard library
// is missing some things in the C system. "std/c.zig" is missing fd_t,
// which causes compilation to fail. So build to wasi instead, until it gets
// fixed.
return std.zig.CrossTarget{
.cpu_arch = target.cpu_arch,
.cpu_model = target.cpu_model,
.cpu_features_add = target.cpu_features_add,
.cpu_features_sub = target.cpu_features_sub,
.os_tag = .wasi,
.os_version_min = target.os_version_min,
.os_version_max = target.os_version_max,
.glibc_version = target.glibc_version,
.abi = target.abi,
.dynamic_linker = target.dynamic_linker,
.ofmt = target.ofmt,
};
}
const webhack_c =
\\// Zig adds '__stack_chk_guard', '__stack_chk_fail', and 'errno',
\\// which emscripten doesn't actually support.
\\// Seems that zig ignores disabling stack checking,
\\// and I honestly don't know why emscripten doesn't have errno.
\\// TODO: when the updateTargetForWeb workaround gets removed, see if those are nessesary anymore
\\#include <stdint.h>
\\uintptr_t __stack_chk_guard;
\\//I'm not certain if this means buffer overflows won't be detected,
\\// However, zig is pretty safe from those, so don't worry about it too much.
\\void __stack_chk_fail(void){}
\\int errno;
;