forked from Not-Nik/raylib-zig
-
Notifications
You must be signed in to change notification settings - Fork 0
/
emcc.zig
139 lines (124 loc) · 5.34 KB
/
emcc.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
// raylib-zig (c) Nikolas Wipper 2020-2024
const std = @import("std");
const builtin = @import("builtin");
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.Build.ResolvedTarget,
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 = b.path(root_source_file),
.target = 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;
}
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;
;