From 7a0d397bafe725128b7c53bf3fb8ff52b81a4581 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 00:44:28 -0600 Subject: [PATCH 01/19] add web support using Emscripten --- .gitignore | 4 +- README.md | 12 +++ build.zig | 202 +++++++++++++++++++++++++++++++++++++++++++-- lib/raylib-zig.zig | 2 +- project_setup.sh | 54 ++++++++---- webbuildhack.c | 28 +++++++ 6 files changed, 277 insertions(+), 25 deletions(-) create mode 100644 webbuildhack.c diff --git a/.gitignore b/.gitignore index ab66a1a..5275153 100755 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ zig-cache/ +zig-out/ .idea/ Project/* - +#This is a file that is auto-generated to make a hack work +**/web_emscripten_entry_point.zig libraylib.a raylib.h raymath.h diff --git a/README.md b/README.md index 3839149..efb063f 100755 --- a/README.md +++ b/README.md @@ -62,6 +62,18 @@ To build all available examples simply `zig build examples`. To list available e + You can copy that folder anywhere you want and edit the source + Run `zig build run` at any time to test your project +## Exporting for web +To export your project for the web, first install emsdk. +Once emsdk is installed, set it up by running + +`emsdk install latest` + +Find the folder where it's installed and run + +`zig build webexport --sysroot [path to emsdk]/upstream/emscripten` + +once that is finished, the exported project should be located at `zig-out/htmlout` + ### When is the binding updated? I plan on updating it every mayor release (2.5, 3.0, etc.). Keep in mind these are technically header files, so any implementation stuff should be updatable with some hacks on your side. diff --git a/build.zig b/build.zig index 531e6ee..3df8afb 100755 --- a/build.zig +++ b/build.zig @@ -2,6 +2,7 @@ const std = @import("std"); const rl = @This(); +const builtin = @import("builtin"); const Program = struct { name: []const u8, @@ -14,7 +15,6 @@ pub fn link(b: *std.Build, exe: *std.Build.Step.Compile, target: std.zig.CrossTa .target = target, .optimize = optimize, }); - var art = raylib.artifact("raylib"); const target_os = exe.target.toTarget().os.tag; @@ -43,6 +43,12 @@ pub fn link(b: *std.Build, exe: *std.Build.Step.Compile, target: std.zig.CrossTa 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"); @@ -64,7 +70,15 @@ pub fn getArtifact(b: *std.Build, target: std.zig.CrossTarget, optimize: std.bui return raylib.artifact("raylib"); } -pub fn getModule(b: *std.Build) *std.Build.Module { +//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" } }); +} + +pub fn getModuleInternal(b: *std.Build) *std.Build.Module { if (b.modules.contains("raylib")) { return b.modules.get("raylib").?; } @@ -72,13 +86,18 @@ pub fn getModule(b: *std.Build) *std.Build.Module { } pub const math = struct { - pub fn getModule(b: *std.Build) *std.Build.Module { - var raylib = rl.getModule(b); + pub fn getModule(b: *std.Build, comptime rl_path: []const u8) *std.Build.Module { + var raylib = rl.getModuleInternal(b); + 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 { +pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); @@ -149,19 +168,184 @@ pub fn build(b: *std.Build) void { const system_lib = b.option(bool, "system-raylib", "link to preinstalled raylib libraries") orelse false; _ = system_lib; - var raylib = rl.getModule(b); - var raylib_math = rl.math.getModule(b); + var raylib = rl.getModuleInternal(b); + var raylib_math = rl.math.getModuleInternal(b); for (examples) |ex| { - const exe = b.addExecutable(.{ .name = ex.name, .root_source_file = .{ .path = ex.path }, .optimize = optimize, .target = target }); + //If compiling for emscripten, things need to be done COMPLETELY differently + if (target.getOsTag() == .emscripten) { + var build_step = try buildForEmscripten(b, ex.name, ex.path, target, optimize, raylib, raylib_math); + var emrun_step = try emscriptenRunStep(b); + emrun_step.step.dependOn(&build_step.step); + var run_step = b.step(ex.name, ex.desc); + run_step.dependOn(&emrun_step.step); + //examples_step.dependOn(&build_step.step); + continue; + } + const exe = b.addExecutable(.{ .name = ex.name, .root_source_file = .{ .path = ex.path }, .optimize = optimize, .target = target }); rl.link(b, exe, target, optimize); exe.addModule("raylib", raylib); exe.addModule("raylib-math", raylib_math); - const run_cmd = b.addRunArtifact(exe); const run_step = b.step(ex.name, ex.desc); run_step.dependOn(&run_cmd.step); examples_step.dependOn(&exe.step); } } + +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", + }; + var emrun_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emrunExe.len + 1); + defer b.allocator.free(emrun_run_arg); + + 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, "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str ++ "index.html" }); + return run_cmd; +} + +pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.Mode, raylib: *std.Build.Module, raylib_math: *std.Build.Module) !*std.Build.Step.Run { + const new_target = updateTargetForWeb(target); + // Emscripten is completely unable to find Zig's entry point. + // Solution: create a C compatible entry point in zig, + // and then a C file that calls that entry point in the main method. + // The C file is pre-written into the project, + // However since the zig file needs to @import the main project, + // it needs to be generated at compile time, + // and it needs to be in the project's source. + + //First, generate the file string + const zig_entrypoint_format = "//This file is generated in order to allow web builds to start properly. It can safely be deleted.\nconst main = @import(\"{s}\");export fn web_emscripten_entry_point() c_int {{main.main() catch {{return -1;}};return 0;}}"; + const zig_entrypoint_buffer = try b.allocator.alloc(u8, zig_entrypoint_format.len + root_source_file.len + 1); + const root_source_file_name = root_source_file[(lastIndexOf(root_source_file, '/') + 1)..]; + const zig_entrypoint_code = try std.fmt.bufPrint(zig_entrypoint_buffer, zig_entrypoint_format, .{root_source_file_name}); + const zig_entrypoint_dir = root_source_file[0..lastIndexOf(root_source_file, '/')]; + const zig_entrypoint_file = try b.allocator.alloc(u8, zig_entrypoint_dir.len + "/web_emscripten_entry_point.zig".len); + _ = try std.fmt.bufPrint(zig_entrypoint_file, "{s}{s}", .{ zig_entrypoint_dir, "/web_emscripten_entry_point.zig" }); + + //TODO: find a way to create a C-compatible entrypoint without doing this + const write_zig_entrypoint = b.addWriteFiles(); + write_zig_entrypoint.addBytesToSource(zig_entrypoint_code, zig_entrypoint_file); + + //create the web back C file + const webhack_c_file_step = b.addWriteFiles(); + const webhack_c_file = webhack_c_file_step.add("webhack.c", webhack_c); + + //the project is built as a library and linked with everything later + const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = new_target, .optimize = optimize }); + exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]const u8{} }); + exe_lib.addModule("raylib", raylib); + exe_lib.addModule("raylib-math", raylib_math); + //link with libc since raylib isn't going to be linked in until later + exe_lib.linkLibC(); + exe_lib.step.dependOn(&write_zig_entrypoint.step); + exe_lib.step.dependOn(&webhack_c_file_step.step); + //Emcc has to be the one that links the project, in order to make sure that the web libraries are correctly linked. + if (b.sysroot == null) { + @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'"); + } + //If compiling on windows , use emcc.bat + 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 }); + + const raylib_artifact = getArtifact(b, target, optimize); + //create the output directory because emcc can't do it. + const mkdir_command = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str }); + //Actually link everything together + const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_run_arg}); + emcc_command.addFileArg(raylib_artifact.getEmittedBin()); + emcc_command.addFileArg(exe_lib.getEmittedBin()); + //TODO: see if there is a way to properly integrate the output directory into zig's cache + // LATER: The way is by zig's writeFile creates place in the cache that can then be used to place emcc's output + // TODO: investigate why "-sSTANDALONE_WASM" completely freezes the page once the program loads and starts. + emcc_command.addArgs(&[_][]const u8{ "-o", "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str ++ "index.html", "-sFULL-ES3=1", "-sUSE_GLFW=3", "-sASYNCIFY", "-O3" }); + emcc_command.step.dependOn(&exe_lib.step); + emcc_command.step.dependOn(&raylib_artifact.step); + emcc_command.step.dependOn(&mkdir_command.step); + + 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; +} +fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { + //zig building to emscripten doesn't really work, + // as the standard library doesn't compile for some reason + // So build to wasi instead. + 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, + }; +} + +pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8) !*std.Build.Step { + // EXPORTING to WEB, the only reasonable output is emscripten ReleaseSafe. + // (Safe because since when was performance the main goal of the internet?) + //Note: the name doesn't matter, it's only used as the file name of a temporary object, so it's just called "project" + const target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = "wasm32-emscripten" }); + const optimize = std.builtin.OptimizeMode.ReleaseSafe; + var raylib = rl.getModule(b, rl_path); + var raylib_math = rl.math.getModule(b, rl_path); + const build_step = try buildForEmscripten(b, "project", root_source_file, target, optimize, raylib, raylib_math); + const run_step = try emscriptenRunStep(b); + run_step.step.dependOn(&build_step.step); + const run_step_arg = b.step("run", "This only exists because the run step for web exports is different"); + run_step_arg.dependOn(&run_step.step); + return &build_step.step; +} +const webhack_c = + \\ // Emscripten is completely unable to find Zig's entry point. + \\ // Solution: create a C compatible entry point in zig, + \\ // and then a C file that calls that entry point in the main method + \\ // This is that C file. + \\ //The entry point found in the zig project + \\ extern int web_emscripten_entry_point(); + \\ //this is the method that emscripten calls automatically when the page loads + \\ int main(int argc, char** argv) + \\ { + \\ //TODO: possibly pass arguments into zig? + \\ return web_emscripten_entry_point(); + \\ } + \\ // For some reason 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. + \\ // there's a solution, although it's not a good one... + \\ #include + \\ uintptr_t __stack_chk_guard; + \\ //Yes, this means that if there is a buffer overflow, nobody is going to know about it. + \\ // However, zig is pretty safe from those, so don't worry about it too much. + \\ void __stack_chk_fail(void){} + \\ int errno; +; diff --git a/lib/raylib-zig.zig b/lib/raylib-zig.zig index dd8054c..009981f 100644 --- a/lib/raylib-zig.zig +++ b/lib/raylib-zig.zig @@ -1296,7 +1296,7 @@ pub fn loadCodepoints(text: [:0]const u8) []i32 { } pub fn textFormat(text: [:0]const u8, args: anytype) [:0]const u8 { - return std.mem.span(@call(.{}, cdef.TextFormat, .{@as([*c]const u8, @ptrCast(text))} ++ args)); + return std.mem.span(@call(.auto, cdef.TextFormat, .{@as([*c]const u8, @ptrCast(text))} ++ args)); } pub fn textSplit(text: [:0]const u8, delimiter: u8) [][:0]const u8 { diff --git a/project_setup.sh b/project_setup.sh index 42c0a9e..e36319d 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -12,24 +12,50 @@ echo "generating project files..." echo 'const std = @import("std"); const rl = @import("raylib-zig/build.zig"); -pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); +pub fn build(b: *std.Build) !void { + const web_export = argsContainsWebexport(b.allocator); + if (!web_export) { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); - var raylib = rl.getModule(b); - var raylib_math = rl.math.getModule(b); + var raylib = rl.getModule(b, "raylib-zig"); + var raylib_math = rl.math.getModule(b, "raylib-zig"); - const exe = b.addExecutable(.{ .name = "'$PROJECT_NAME'", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target }); - - rl.link(b, exe, target, optimize); - exe.addModule("raylib", raylib); - exe.addModule("raylib-math", raylib_math); + const exe = b.addExecutable(.{ .name = "'$PROJECT_NAME'", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target }); - const run_cmd = b.addRunArtifact(exe); - const run_step = b.step("run", "Run '$PROJECT_NAME'"); - run_step.dependOn(&run_cmd.step); + rl.link(b, exe, target, optimize); + exe.addModule("raylib", raylib); + exe.addModule("raylib-math", raylib_math); - b.installArtifact(exe); + const run_cmd = b.addRunArtifact(exe); + const run_step = b.step("run", "Run '$PROJECT_NAME'"); + run_step.dependOn(&run_cmd.step); + + b.installArtifact(exe); + } + + //web exports are completely separate, due to the amount of hackery required. + const export_step = b.step("webexport", "Export '$PROJECT_NAME' for the web"); + if (web_export) { + export_step.dependOn(try rl.webExport(b, "src/main.zig", "raylib-zig")); + } + + // Building for web requires a --sysroot [emscripten and stuff] + // But asking for that for all builds is not a good user experience + // So it will only actually set up the build scripts if the web export is actually going to happen. +} + +fn argsContainsWebexport(allocator: std.mem.Allocator) bool { + const args = std.process.argsAlloc(allocator) catch { + return false; + }; + defer allocator.free(args); + for (args) |arg| { + if (std.mem.eql(u8, "webexport", arg)) { + return true; + } + } + return false; } ' >> build.zig diff --git a/webbuildhack.c b/webbuildhack.c new file mode 100644 index 0000000..f179218 --- /dev/null +++ b/webbuildhack.c @@ -0,0 +1,28 @@ +// Emscripten is completely unable to find Zig's entry point. +// Solution: create a C compatible entry point in zig, +// and then a C file that calls that entry point in the main method +// This is that C file. + +//The entry point found in the zig project +extern int web_emscripten_entry_point(); +//this is the method that emscripten calls automatically when the page loads +int main(int argc, char** argv) +{ + //TODO: possibly pass arguments into zig? + return web_emscripten_entry_point(); +} + +// For some reason 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. +// there's a solution, although it's not a good one... +#include + +uintptr_t __stack_chk_guard; + +//Yes, this means that if there is a buffer overflow, nobody is going to know about it. +// However, zig is pretty safe from those, so don't worry about it too much. +void __stack_chk_fail(void){} + +int errno; From c76acc242da2726365303923c422e090f146166a Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 01:01:08 -0600 Subject: [PATCH 02/19] fix mistake --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 3df8afb..5571f93 100755 --- a/build.zig +++ b/build.zig @@ -87,7 +87,7 @@ pub fn getModuleInternal(b: *std.Build) *std.Build.Module { pub const math = struct { pub fn getModule(b: *std.Build, comptime rl_path: []const u8) *std.Build.Module { - var raylib = rl.getModuleInternal(b); + 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 }} }); } From 6d5722ba358ea054cdada18e25a1a39a9a110137 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 01:05:06 -0600 Subject: [PATCH 03/19] remove unnecesary file --- webbuildhack.c | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 webbuildhack.c diff --git a/webbuildhack.c b/webbuildhack.c deleted file mode 100644 index f179218..0000000 --- a/webbuildhack.c +++ /dev/null @@ -1,28 +0,0 @@ -// Emscripten is completely unable to find Zig's entry point. -// Solution: create a C compatible entry point in zig, -// and then a C file that calls that entry point in the main method -// This is that C file. - -//The entry point found in the zig project -extern int web_emscripten_entry_point(); -//this is the method that emscripten calls automatically when the page loads -int main(int argc, char** argv) -{ - //TODO: possibly pass arguments into zig? - return web_emscripten_entry_point(); -} - -// For some reason 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. -// there's a solution, although it's not a good one... -#include - -uintptr_t __stack_chk_guard; - -//Yes, this means that if there is a buffer overflow, nobody is going to know about it. -// However, zig is pretty safe from those, so don't worry about it too much. -void __stack_chk_fail(void){} - -int errno; From 5efb75c29151079c4e8e186fe178be9ec05a8316 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 10:57:27 -0600 Subject: [PATCH 04/19] project buils based on target instead of webexport --- README.md | 2 +- build.zig | 3 +-- project_setup.sh | 27 ++++----------------------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index efb063f..eca8f18 100755 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Once emsdk is installed, set it up by running Find the folder where it's installed and run -`zig build webexport --sysroot [path to emsdk]/upstream/emscripten` +`zig build -Dtarget=wasm32-emscripten --sysroot [path to emsdk]/upstream/emscripten` once that is finished, the exported project should be located at `zig-out/htmlout` diff --git a/build.zig b/build.zig index 5571f93..38e7d35 100755 --- a/build.zig +++ b/build.zig @@ -309,12 +309,11 @@ fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { }; } -pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8) !*std.Build.Step { +pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8, optimize: std.builtin.OptimizeMode) !*std.Build.Step { // EXPORTING to WEB, the only reasonable output is emscripten ReleaseSafe. // (Safe because since when was performance the main goal of the internet?) //Note: the name doesn't matter, it's only used as the file name of a temporary object, so it's just called "project" const target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = "wasm32-emscripten" }); - const optimize = std.builtin.OptimizeMode.ReleaseSafe; var raylib = rl.getModule(b, rl_path); var raylib_math = rl.math.getModule(b, rl_path); const build_step = try buildForEmscripten(b, "project", root_source_file, target, optimize, raylib, raylib_math); diff --git a/project_setup.sh b/project_setup.sh index e36319d..ed033e4 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -13,11 +13,10 @@ echo 'const std = @import("std"); const rl = @import("raylib-zig/build.zig"); pub fn build(b: *std.Build) !void { - const web_export = argsContainsWebexport(b.allocator); + const target = b.standardTargetOptions(.{}); + const web_export = target.getOsTag() == .emscripten; + const optimize = b.standardOptimizeOption(.{}); if (!web_export) { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - var raylib = rl.getModule(b, "raylib-zig"); var raylib_math = rl.math.getModule(b, "raylib-zig"); @@ -35,27 +34,9 @@ pub fn build(b: *std.Build) !void { } //web exports are completely separate, due to the amount of hackery required. - const export_step = b.step("webexport", "Export '$PROJECT_NAME' for the web"); if (web_export) { - export_step.dependOn(try rl.webExport(b, "src/main.zig", "raylib-zig")); + try rl.webExport(b, "src/main.zig", "raylib-zig", optimize); } - - // Building for web requires a --sysroot [emscripten and stuff] - // But asking for that for all builds is not a good user experience - // So it will only actually set up the build scripts if the web export is actually going to happen. -} - -fn argsContainsWebexport(allocator: std.mem.Allocator) bool { - const args = std.process.argsAlloc(allocator) catch { - return false; - }; - defer allocator.free(args); - for (args) |arg| { - if (std.mem.eql(u8, "webexport", arg)) { - return true; - } - } - return false; } ' >> build.zig From 7c40bb61766e40de0312f91cdc2aa68bccad4a6a Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 11:00:40 -0600 Subject: [PATCH 05/19] fix compile error in project setup --- project_setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project_setup.sh b/project_setup.sh index ed033e4..fc78832 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -35,7 +35,7 @@ pub fn build(b: *std.Build) !void { //web exports are completely separate, due to the amount of hackery required. if (web_export) { - try rl.webExport(b, "src/main.zig", "raylib-zig", optimize); + b.getInstallStep().dependOn(try rl.webExport(b, "src/main.zig", "raylib-zig", optimize)); } } ' >> build.zig From 7120d454fdc0122be9ebec04fde6cb59b6cf2dc0 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 11:03:30 -0600 Subject: [PATCH 06/19] remove updateTargetForWeb --- build.zig | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/build.zig b/build.zig index 38e7d35..ee817f1 100755 --- a/build.zig +++ b/build.zig @@ -214,7 +214,6 @@ pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { } pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.Mode, raylib: *std.Build.Module, raylib_math: *std.Build.Module) !*std.Build.Step.Run { - const new_target = updateTargetForWeb(target); // Emscripten is completely unable to find Zig's entry point. // Solution: create a C compatible entry point in zig, // and then a C file that calls that entry point in the main method. @@ -241,7 +240,7 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c const webhack_c_file = webhack_c_file_step.add("webhack.c", webhack_c); //the project is built as a library and linked with everything later - const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = new_target, .optimize = optimize }); + const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = target, .optimize = optimize }); exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]const u8{} }); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); @@ -290,24 +289,6 @@ fn lastIndexOf(string: []const u8, character: u8) usize { } return string.len - 1; } -fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { - //zig building to emscripten doesn't really work, - // as the standard library doesn't compile for some reason - // So build to wasi instead. - 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, - }; -} pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8, optimize: std.builtin.OptimizeMode) !*std.Build.Step { // EXPORTING to WEB, the only reasonable output is emscripten ReleaseSafe. From 7c296e86678a23669840e8e73e117ab1791bbff9 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 11:36:44 -0600 Subject: [PATCH 07/19] add updateTargetForWeb back --- build.zig | 29 +++++++++++++++++++++++++---- project_setup.sh | 35 ++++++++++++++++------------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/build.zig b/build.zig index ee817f1..2da52bd 100755 --- a/build.zig +++ b/build.zig @@ -214,6 +214,7 @@ pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { } pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.Mode, raylib: *std.Build.Module, raylib_math: *std.Build.Module) !*std.Build.Step.Run { + const new_target = updateTargetForWeb(target); // Emscripten is completely unable to find Zig's entry point. // Solution: create a C compatible entry point in zig, // and then a C file that calls that entry point in the main method. @@ -240,14 +241,15 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c const webhack_c_file = webhack_c_file_step.add("webhack.c", webhack_c); //the project is built as a library and linked with everything later - const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = target, .optimize = optimize }); + const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = new_target, .optimize = optimize }); exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]const u8{} }); + const raylib_artifact = getArtifact(b, target, optimize); + exe_lib.linkLibrary(raylib_artifact); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); - //link with libc since raylib isn't going to be linked in until later - exe_lib.linkLibC(); exe_lib.step.dependOn(&write_zig_entrypoint.step); exe_lib.step.dependOn(&webhack_c_file_step.step); + exe_lib.step.dependOn(&raylib_artifact.step); //Emcc has to be the one that links the project, in order to make sure that the web libraries are correctly linked. if (b.sysroot == null) { @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'"); @@ -262,7 +264,6 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c emcc_run_arg = try std.fmt.bufPrint(emcc_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ b.sysroot.?, emccExe }); - const raylib_artifact = getArtifact(b, target, optimize); //create the output directory because emcc can't do it. const mkdir_command = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str }); //Actually link everything together @@ -289,6 +290,26 @@ fn lastIndexOf(string: []const u8, character: u8) usize { } return string.len - 1; } +// TODO: each zig update, remove this and see if everything still works. +// TODO: submit an issue to zig's repo to fix the problem that this works around +fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { + //zig building to emscripten doesn't really work, + // as the standard library doesn't compile for some reason + // So build to wasi instead. + 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, + }; +} pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8, optimize: std.builtin.OptimizeMode) !*std.Build.Step { // EXPORTING to WEB, the only reasonable output is emscripten ReleaseSafe. diff --git a/project_setup.sh b/project_setup.sh index fc78832..443a50e 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -14,29 +14,26 @@ const rl = @import("raylib-zig/build.zig"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); - const web_export = target.getOsTag() == .emscripten; const optimize = b.standardOptimizeOption(.{}); - if (!web_export) { - var raylib = rl.getModule(b, "raylib-zig"); - var raylib_math = rl.math.getModule(b, "raylib-zig"); - - const exe = b.addExecutable(.{ .name = "'$PROJECT_NAME'", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target }); - - rl.link(b, exe, target, optimize); - exe.addModule("raylib", raylib); - exe.addModule("raylib-math", raylib_math); - - const run_cmd = b.addRunArtifact(exe); - const run_step = b.step("run", "Run '$PROJECT_NAME'"); - run_step.dependOn(&run_cmd.step); - - b.installArtifact(exe); - } - //web exports are completely separate, due to the amount of hackery required. - if (web_export) { + if (target.getOsTag()) { b.getInstallStep().dependOn(try rl.webExport(b, "src/main.zig", "raylib-zig", optimize)); + return; } + var raylib = rl.getModule(b, "raylib-zig"); + var raylib_math = rl.math.getModule(b, "raylib-zig"); + + const exe = b.addExecutable(.{ .name = "'$PROJECT_NAME'", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target }); + + rl.link(b, exe, target, optimize); + exe.addModule("raylib", raylib); + exe.addModule("raylib-math", raylib_math); + + const run_cmd = b.addRunArtifact(exe); + const run_step = b.step("run", "Run '$PROJECT_NAME'"); + run_step.dependOn(&run_cmd.step); + + b.installArtifact(exe); } ' >> build.zig From 6e6cbceef7ca8dc68e4c8066156e50ccd04fef8c Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 11:46:32 -0600 Subject: [PATCH 08/19] make updateTargetForWeb reasons more clear --- build.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 2da52bd..1a9fbaa 100755 --- a/build.zig +++ b/build.zig @@ -293,9 +293,9 @@ fn lastIndexOf(string: []const u8, character: u8) usize { // TODO: each zig update, remove this and see if everything still works. // TODO: submit an issue to zig's repo to fix the problem that this works around fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { - //zig building to emscripten doesn't really work, - // as the standard library doesn't compile for some reason - // So build to wasi instead. + //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, From a28f4758502d96a450f176cb89a5996661c3bbb9 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 12:08:37 -0600 Subject: [PATCH 09/19] remove a TODO --- build.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 1a9fbaa..6c911c4 100755 --- a/build.zig +++ b/build.zig @@ -291,7 +291,7 @@ fn lastIndexOf(string: []const u8, character: u8) usize { return string.len - 1; } // TODO: each zig update, remove this and see if everything still works. -// TODO: submit an issue to zig's repo to fix the problem that this works around +// 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. @@ -312,12 +312,10 @@ fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { } pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8, optimize: std.builtin.OptimizeMode) !*std.Build.Step { - // EXPORTING to WEB, the only reasonable output is emscripten ReleaseSafe. - // (Safe because since when was performance the main goal of the internet?) - //Note: the name doesn't matter, it's only used as the file name of a temporary object, so it's just called "project" const target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = "wasm32-emscripten" }); var raylib = rl.getModule(b, rl_path); var raylib_math = rl.math.getModule(b, rl_path); + //Note: the name doesn't matter, it's only used as the file name of a temporary object, so it's just called "project" const build_step = try buildForEmscripten(b, "project", root_source_file, target, optimize, raylib, raylib_math); const run_step = try emscriptenRunStep(b); run_step.step.dependOn(&build_step.step); From 2c362b9b72f49313aef0b00b6a912295a885d352 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 12:41:52 -0600 Subject: [PATCH 10/19] update readme to avoid confusion --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eca8f18..a454f3e 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Manually tweaked, auto-generated [raylib](https://github.com/raysan5/raylib) bindings for zig. -Bindings tested on raylib version 4.5.0-dev and Zig 0.10.1 +Bindings tested on raylib version 4.5.0-dev and Zig 0.11.0 Thanks to all the [contributors](https://github.com/Not-Nik/raylib-zig/graphs/contributors) for their help with this binding. From dea3072cc8f206afaf9146dbf9510f0bfc86a427 Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 15:55:51 -0600 Subject: [PATCH 11/19] add error print and various minor code changes --- build.zig | 113 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 41 deletions(-) diff --git a/build.zig b/build.zig index 6c911c4..0f53bdc 100755 --- a/build.zig +++ b/build.zig @@ -204,10 +204,10 @@ pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { .windows => "emrun.bat", else => "emrun", }; - var emrun_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emrunExe.len + 1); + const emrun_run_arg = try b.allocator.alloc(u8, b.sysroot.?.len + emrunExe.len + 1); defer b.allocator.free(emrun_run_arg); - emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ b.sysroot.?, emrunExe }); + _ = 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, "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str ++ "index.html" }); return run_cmd; @@ -223,37 +223,67 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c // it needs to be generated at compile time, // and it needs to be in the project's source. - //First, generate the file string - const zig_entrypoint_format = "//This file is generated in order to allow web builds to start properly. It can safely be deleted.\nconst main = @import(\"{s}\");export fn web_emscripten_entry_point() c_int {{main.main() catch {{return -1;}};return 0;}}"; - const zig_entrypoint_buffer = try b.allocator.alloc(u8, zig_entrypoint_format.len + root_source_file.len + 1); - const root_source_file_name = root_source_file[(lastIndexOf(root_source_file, '/') + 1)..]; - const zig_entrypoint_code = try std.fmt.bufPrint(zig_entrypoint_buffer, zig_entrypoint_format, .{root_source_file_name}); + //TODO: find a way to create a C-compatible entrypoint without doing this + + //Figure out where to write the file + //TODO: This does mean that building all of the examples at once would cause them to clash, + // However for now that is not a problem const zig_entrypoint_dir = root_source_file[0..lastIndexOf(root_source_file, '/')]; const zig_entrypoint_file = try b.allocator.alloc(u8, zig_entrypoint_dir.len + "/web_emscripten_entry_point.zig".len); _ = try std.fmt.bufPrint(zig_entrypoint_file, "{s}{s}", .{ zig_entrypoint_dir, "/web_emscripten_entry_point.zig" }); - //TODO: find a way to create a C-compatible entrypoint without doing this + //create the file + const zig_entrypoint_format = + \\//This file is generated in order to allow web builds to start properly. It can safely be deleted. + \\const main = @import("{s}"); + \\const std = @import("std"); + \\export fn web_emscripten_entry_point() c_int + \\{{ + \\ main.main() catch |e| {{ + \\ //Since emscripten isn't going to get the stack trace, + \\ // it needs to be printed elsewhere + \\ const writer = std.io.getStdErr().writer(); + \\ //TODO: It seems zig doesn't put error info into wasm binaries: https://github.com/ziglang/zig/issues/10426 + \\ // So this always returns null. + \\ // if (@errorReturnTrace()) |trace| { + \\ // trace.format("{s}", .{}, writer); + \\ // } + \\ // I'm not really worried since web outputs are usually reserved for release anyway, which by then most errors should be fixed. + \\ writer.print("THERE WAS AN ERROR:{{s}}\n", .{{@errorName(e)}}) catch {{}}; + \\ }}; + \\ return 0; + \\}} + ; + const zig_entrypoint_buffer = try b.allocator.alloc(u8, zig_entrypoint_format.len + root_source_file.len + 1); + const root_source_file_name = root_source_file[(lastIndexOf(root_source_file, '/') + 1)..]; + const zig_entrypoint_code = try std.fmt.bufPrint(zig_entrypoint_buffer, zig_entrypoint_format, .{root_source_file_name}); + const write_zig_entrypoint = b.addWriteFiles(); write_zig_entrypoint.addBytesToSource(zig_entrypoint_code, zig_entrypoint_file); - //create the web back C file + //create the entrypoint C file - unlike the zig file, this can be placed anywhere, so it goes into the cache + // It's worth noting that this also defines some symbols that zig adds for wasi that emscripten doesn't support. const webhack_c_file_step = b.addWriteFiles(); const webhack_c_file = webhack_c_file_step.add("webhack.c", webhack_c); - //the project is built as a library and linked with everything later + //Emcc has to be the one that links the project, in order to make sure that the web libraries are correctly linked. + if (b.sysroot == null) { + @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'"); + } + //the project is built as a library and linked later const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = new_target, .optimize = optimize }); - exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]const u8{} }); + const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); + defer b.allocator.free(cache_include); + exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]u8{} }); const raylib_artifact = getArtifact(b, target, optimize); + //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.linkLibrary(raylib_artifact); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); exe_lib.step.dependOn(&write_zig_entrypoint.step); exe_lib.step.dependOn(&webhack_c_file_step.step); exe_lib.step.dependOn(&raylib_artifact.step); - //Emcc has to be the one that links the project, in order to make sure that the web libraries are correctly linked. - if (b.sysroot == null) { - @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'"); - } + //If compiling on windows , use emcc.bat const emccExe = switch (builtin.os.tag) { .windows => "emcc.bat", @@ -270,9 +300,8 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_run_arg}); emcc_command.addFileArg(raylib_artifact.getEmittedBin()); emcc_command.addFileArg(exe_lib.getEmittedBin()); - //TODO: see if there is a way to properly integrate the output directory into zig's cache - // LATER: The way is by zig's writeFile creates place in the cache that can then be used to place emcc's output - // TODO: investigate why "-sSTANDALONE_WASM" completely freezes the page once the program loads and starts. + //TODO: This does mean that building all of the examples at once would cause them to clash, + // However for now that is not a problem emcc_command.addArgs(&[_][]const u8{ "-o", "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str ++ "index.html", "-sFULL-ES3=1", "-sUSE_GLFW=3", "-sASYNCIFY", "-O3" }); emcc_command.step.dependOn(&exe_lib.step); emcc_command.step.dependOn(&raylib_artifact.step); @@ -324,27 +353,29 @@ pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: return &build_step.step; } const webhack_c = - \\ // Emscripten is completely unable to find Zig's entry point. - \\ // Solution: create a C compatible entry point in zig, - \\ // and then a C file that calls that entry point in the main method - \\ // This is that C file. - \\ //The entry point found in the zig project - \\ extern int web_emscripten_entry_point(); - \\ //this is the method that emscripten calls automatically when the page loads - \\ int main(int argc, char** argv) - \\ { - \\ //TODO: possibly pass arguments into zig? - \\ return web_emscripten_entry_point(); - \\ } - \\ // For some reason 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. - \\ // there's a solution, although it's not a good one... - \\ #include - \\ uintptr_t __stack_chk_guard; - \\ //Yes, this means that if there is a buffer overflow, nobody is going to know about it. - \\ // However, zig is pretty safe from those, so don't worry about it too much. - \\ void __stack_chk_fail(void){} - \\ int errno; + \\// Emscripten is completely unable to find Zig's entry point. + \\// Solution: create a C compatible entry point in zig, + \\// and then a C file that calls that entry point in the main method + \\// This is that C file. + \\#include + \\//The entry point found in the zig project + \\extern int web_emscripten_entry_point(); + \\//this is the method that emscripten calls automatically when the page loads + \\int main(int argc, char** argv) + \\{ + \\ //TODO: possibly pass arguments into zig? + \\ return web_emscripten_entry_point(); + \\} + \\// 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 + \\// there's a solution, although it's not a good one... + \\#include + \\uintptr_t __stack_chk_guard; + \\//Yes, this means that if there is a buffer overflow, nobody is going to know about it. + \\// However, zig is pretty safe from those, so don't worry about it too much. + \\void __stack_chk_fail(void){} + \\int errno; ; From b48c7f9219ad3b835cf65987914fa9ce3290515e Mon Sep 17 00:00:00 2001 From: Blue Date: Fri, 11 Aug 2023 16:02:55 -0600 Subject: [PATCH 12/19] fix errors --- build.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 0f53bdc..b4f13ac 100755 --- a/build.zig +++ b/build.zig @@ -245,9 +245,9 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c \\ const writer = std.io.getStdErr().writer(); \\ //TODO: It seems zig doesn't put error info into wasm binaries: https://github.com/ziglang/zig/issues/10426 \\ // So this always returns null. - \\ // if (@errorReturnTrace()) |trace| { - \\ // trace.format("{s}", .{}, writer); - \\ // } + \\ // if (@errorReturnTrace()) |trace| {{ + \\ // trace.format("{{s}}", .{{}}, writer); + \\ // }} \\ // I'm not really worried since web outputs are usually reserved for release anyway, which by then most errors should be fixed. \\ writer.print("THERE WAS AN ERROR:{{s}}\n", .{{@errorName(e)}}) catch {{}}; \\ }}; @@ -364,7 +364,11 @@ const webhack_c = \\int main(int argc, char** argv) \\{ \\ //TODO: possibly pass arguments into zig? - \\ return web_emscripten_entry_point(); + \\ int status = web_emscripten_entry_point(); + \\ //emscripten won't print until a newline is formed, and often people forget the last "\n" of a print. + \\ // Missing lines can be really annoying, so to avoid that one last line is printed before the program exits. + \\ printf("\n"); + \\ return status; \\} \\// Zig adds '__stack_chk_guard', '__stack_chk_fail', and 'errno', \\// which emscripten doesn't actually support. From 7bad5e92bf49dfc71545416075ada41ae9f25117 Mon Sep 17 00:00:00 2001 From: Blue Date: Sun, 13 Aug 2023 15:47:00 -0600 Subject: [PATCH 13/19] remove 'web_build_zig_entrypoint.zig' insanity --- build.zig | 68 ++----------------------------------------------------- 1 file changed, 2 insertions(+), 66 deletions(-) diff --git a/build.zig b/build.zig index b4f13ac..f33d0cc 100755 --- a/build.zig +++ b/build.zig @@ -215,54 +215,8 @@ pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.Mode, raylib: *std.Build.Module, raylib_math: *std.Build.Module) !*std.Build.Step.Run { const new_target = updateTargetForWeb(target); - // Emscripten is completely unable to find Zig's entry point. - // Solution: create a C compatible entry point in zig, - // and then a C file that calls that entry point in the main method. - // The C file is pre-written into the project, - // However since the zig file needs to @import the main project, - // it needs to be generated at compile time, - // and it needs to be in the project's source. - //TODO: find a way to create a C-compatible entrypoint without doing this - - //Figure out where to write the file - //TODO: This does mean that building all of the examples at once would cause them to clash, - // However for now that is not a problem - const zig_entrypoint_dir = root_source_file[0..lastIndexOf(root_source_file, '/')]; - const zig_entrypoint_file = try b.allocator.alloc(u8, zig_entrypoint_dir.len + "/web_emscripten_entry_point.zig".len); - _ = try std.fmt.bufPrint(zig_entrypoint_file, "{s}{s}", .{ zig_entrypoint_dir, "/web_emscripten_entry_point.zig" }); - - //create the file - const zig_entrypoint_format = - \\//This file is generated in order to allow web builds to start properly. It can safely be deleted. - \\const main = @import("{s}"); - \\const std = @import("std"); - \\export fn web_emscripten_entry_point() c_int - \\{{ - \\ main.main() catch |e| {{ - \\ //Since emscripten isn't going to get the stack trace, - \\ // it needs to be printed elsewhere - \\ const writer = std.io.getStdErr().writer(); - \\ //TODO: It seems zig doesn't put error info into wasm binaries: https://github.com/ziglang/zig/issues/10426 - \\ // So this always returns null. - \\ // if (@errorReturnTrace()) |trace| {{ - \\ // trace.format("{{s}}", .{{}}, writer); - \\ // }} - \\ // I'm not really worried since web outputs are usually reserved for release anyway, which by then most errors should be fixed. - \\ writer.print("THERE WAS AN ERROR:{{s}}\n", .{{@errorName(e)}}) catch {{}}; - \\ }}; - \\ return 0; - \\}} - ; - const zig_entrypoint_buffer = try b.allocator.alloc(u8, zig_entrypoint_format.len + root_source_file.len + 1); - const root_source_file_name = root_source_file[(lastIndexOf(root_source_file, '/') + 1)..]; - const zig_entrypoint_code = try std.fmt.bufPrint(zig_entrypoint_buffer, zig_entrypoint_format, .{root_source_file_name}); - - const write_zig_entrypoint = b.addWriteFiles(); - write_zig_entrypoint.addBytesToSource(zig_entrypoint_code, zig_entrypoint_file); - - //create the entrypoint C file - unlike the zig file, this can be placed anywhere, so it goes into the cache - // It's worth noting that this also defines some symbols that zig adds for wasi that emscripten doesn't support. + //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); @@ -271,7 +225,7 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c @panic("Pass '--sysroot \"[path to emsdk installation]/upstream/emscripten\"'"); } //the project is built as a library and linked later - const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = zig_entrypoint_file }, .target = new_target, .optimize = optimize }); + const exe_lib = b.addStaticLibrary(.{ .name = name, .root_source_file = .{ .path = root_source_file }, .target = new_target, .optimize = optimize }); const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); defer b.allocator.free(cache_include); exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]u8{} }); @@ -280,7 +234,6 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c exe_lib.linkLibrary(raylib_artifact); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); - exe_lib.step.dependOn(&write_zig_entrypoint.step); exe_lib.step.dependOn(&webhack_c_file_step.step); exe_lib.step.dependOn(&raylib_artifact.step); @@ -353,23 +306,6 @@ pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: return &build_step.step; } const webhack_c = - \\// Emscripten is completely unable to find Zig's entry point. - \\// Solution: create a C compatible entry point in zig, - \\// and then a C file that calls that entry point in the main method - \\// This is that C file. - \\#include - \\//The entry point found in the zig project - \\extern int web_emscripten_entry_point(); - \\//this is the method that emscripten calls automatically when the page loads - \\int main(int argc, char** argv) - \\{ - \\ //TODO: possibly pass arguments into zig? - \\ int status = web_emscripten_entry_point(); - \\ //emscripten won't print until a newline is formed, and often people forget the last "\n" of a print. - \\ // Missing lines can be really annoying, so to avoid that one last line is printed before the program exits. - \\ printf("\n"); - \\ return status; - \\} \\// Zig adds '__stack_chk_guard', '__stack_chk_fail', and 'errno', \\// which emscripten doesn't actually support. \\// Seems that zig ignores disabling stack checking, From de4918cb56d301d62387fbc2177744da095d0746 Mon Sep 17 00:00:00 2001 From: Blue Date: Sun, 13 Aug 2023 23:52:52 -0600 Subject: [PATCH 14/19] major refactor ohw web build system --- build.zig | 123 +++++++++++++++++++++++++---------------------- project_setup.sh | 22 +++++++-- 2 files changed, 82 insertions(+), 63 deletions(-) diff --git a/build.zig b/build.zig index f33d0cc..bb5ddb2 100755 --- a/build.zig +++ b/build.zig @@ -172,28 +172,35 @@ pub fn build(b: *std.Build) !void { var raylib_math = rl.math.getModuleInternal(b); for (examples) |ex| { - //If compiling for emscripten, things need to be done COMPLETELY differently if (target.getOsTag() == .emscripten) { - var build_step = try buildForEmscripten(b, ex.name, ex.path, target, optimize, raylib, raylib_math); - var emrun_step = try emscriptenRunStep(b); - emrun_step.step.dependOn(&build_step.step); - var run_step = b.step(ex.name, ex.desc); - run_step.dependOn(&emrun_step.step); - //examples_step.dependOn(&build_step.step); - continue; + const exe_lib = compileForEmscripten(b, ex.name, ex.path, target, optimize); + exe_lib.addModule("raylib", raylib); + exe_lib.addModule("raylib-math", raylib_math); + 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 }, &[_]std.Build.LazyPath{.{ .path = "resources/" }}); + link_step.step.dependOn(&raylib_artifact.step); + link_step.step.dependOn(&exe_lib.step); + const run_step = try emscriptenRunStep(b); + run_step.step.dependOn(&link_step.step); + const run_option = b.step(ex.name, ex.desc); + run_option.dependOn(&run_step.step); + } else { + const exe = b.addExecutable(.{ .name = ex.name, .root_source_file = .{ .path = ex.path }, .optimize = optimize, .target = target }); + rl.link(b, exe, target, optimize); + exe.addModule("raylib", raylib); + exe.addModule("raylib-math", raylib_math); + const run_cmd = b.addRunArtifact(exe); + const run_step = b.step(ex.name, ex.desc); + run_step.dependOn(&run_cmd.step); + examples_step.dependOn(&exe.step); } - - const exe = b.addExecutable(.{ .name = ex.name, .root_source_file = .{ .path = ex.path }, .optimize = optimize, .target = target }); - rl.link(b, exe, target, optimize); - exe.addModule("raylib", raylib); - exe.addModule("raylib-math", raylib_math); - const run_cmd = b.addRunArtifact(exe); - const run_step = b.step(ex.name, ex.desc); - run_step.dependOn(&run_cmd.step); - examples_step.dependOn(&exe.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) { @@ -209,35 +216,44 @@ pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { _ = 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, "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str ++ "index.html" }); + const run_cmd = b.addSystemCommand(&[_][]const u8{ emrun_run_arg, emccOutputDir ++ emccOutputFile }); return run_cmd; } -pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []const u8, target: std.zig.CrossTarget, optimize: std.builtin.Mode, raylib: *std.Build.Module, raylib_math: *std.Build.Module) !*std.Build.Step.Run { +//Creates the static library to build a project for Emscripten +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; +} - //Emcc has to be the one that links the project, in order to make sure that the web libraries are correctly linked. +//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 way to convert from an input path to the output path, if emscripten even allows such a thing. +// TODO: add a parameter that allows a custom output directory +fn linkWithEmscripten(b: *std.Build, itemsToLink: []const *std.Build.Step.Compile, filesToInclude: []const std.Build.LazyPath) !*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\"'"); } - //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 }); - const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); - defer b.allocator.free(cache_include); - exe_lib.addCSourceFile(.{ .file = webhack_c_file, .flags = &[_][]u8{} }); - const raylib_artifact = getArtifact(b, target, optimize); - //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.linkLibrary(raylib_artifact); - exe_lib.addModule("raylib", raylib); - exe_lib.addModule("raylib-math", raylib_math); - exe_lib.step.dependOn(&webhack_c_file_step.step); - exe_lib.step.dependOn(&raylib_artifact.step); - - //If compiling on windows , use emcc.bat const emccExe = switch (builtin.os.tag) { .windows => "emcc.bat", else => "emcc", @@ -248,18 +264,23 @@ pub fn buildForEmscripten(b: *std.Build, name: []const u8, root_source_file: []c 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", "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str }); + const mkdir_command = b.addSystemCommand(&[_][]const u8{ "mkdir", "-p", emccOutputDir }); //Actually link everything together const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_run_arg}); - emcc_command.addFileArg(raylib_artifact.getEmittedBin()); - emcc_command.addFileArg(exe_lib.getEmittedBin()); - //TODO: This does mean that building all of the examples at once would cause them to clash, - // However for now that is not a problem - emcc_command.addArgs(&[_][]const u8{ "-o", "zig-out" ++ std.fs.path.sep_str ++ "htmlout" ++ std.fs.path.sep_str ++ "index.html", "-sFULL-ES3=1", "-sUSE_GLFW=3", "-sASYNCIFY", "-O3" }); - emcc_command.step.dependOn(&exe_lib.step); - emcc_command.step.dependOn(&raylib_artifact.step); - emcc_command.step.dependOn(&mkdir_command.step); + 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" }); + + //Web builds can't just have the files next to the executable, they have to be linked with the program + for (filesToInclude) |file| { + emcc_command.addArg("--embed-file"); + emcc_command.addArg(file.path); + } return emcc_command; } @@ -292,29 +313,15 @@ fn updateTargetForWeb(target: std.zig.CrossTarget) std.zig.CrossTarget { .ofmt = target.ofmt, }; } - -pub fn webExport(b: *std.Build, root_source_file: []const u8, comptime rl_path: []const u8, optimize: std.builtin.OptimizeMode) !*std.Build.Step { - const target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = "wasm32-emscripten" }); - var raylib = rl.getModule(b, rl_path); - var raylib_math = rl.math.getModule(b, rl_path); - //Note: the name doesn't matter, it's only used as the file name of a temporary object, so it's just called "project" - const build_step = try buildForEmscripten(b, "project", root_source_file, target, optimize, raylib, raylib_math); - const run_step = try emscriptenRunStep(b); - run_step.step.dependOn(&build_step.step); - const run_step_arg = b.step("run", "This only exists because the run step for web exports is different"); - run_step_arg.dependOn(&run_step.step); - return &build_step.step; -} 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 - \\// there's a solution, although it's not a good one... \\#include \\uintptr_t __stack_chk_guard; - \\//Yes, this means that if there is a buffer overflow, nobody is going to know about it. + \\//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; diff --git a/project_setup.sh b/project_setup.sh index 443a50e..c22d242 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -15,13 +15,25 @@ const rl = @import("raylib-zig/build.zig"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - //web exports are completely separate, due to the amount of hackery required. - if (target.getOsTag()) { - b.getInstallStep().dependOn(try rl.webExport(b, "src/main.zig", "raylib-zig", optimize)); - return; - } var raylib = rl.getModule(b, "raylib-zig"); var raylib_math = rl.math.getModule(b, "raylib-zig"); + //web exports are completely separate + if (target.getOsTag() == .emscripten) { + const exe_lib = compileForEmscripten(b, '$PROJECT_NAME', "src/main.zig", target, optimize); + exe_lib.addModule("raylib", raylib); + exe_lib.addModule("raylib-math", raylib_math); + const raylib_artifact = rl.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 }, &[_]std.Build.LazyPath{.{ .path = "resources/" }}); + link_step.step.dependOn(&raylib_artifact.step); + link_step.step.dependOn(&exe_lib.step); + b.getInstallStep.dependOn(&link_step.step); + const run_step = try emscriptenRunStep(b); + run_step.step.dependOn(&link_step.step); + const run_option = b.step("run", "Run '$PROJECT_NAME'"); + run_option.dependOn(&run_step.step); + } const exe = b.addExecutable(.{ .name = "'$PROJECT_NAME'", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target }); From c4de1da7b12cc8b56ed0b5130c47cedb489de9b0 Mon Sep 17 00:00:00 2001 From: Blue Date: Sun, 13 Aug 2023 23:53:13 -0600 Subject: [PATCH 15/19] fix issue --- project_setup.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/project_setup.sh b/project_setup.sh index c22d242..4b6f37d 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -33,6 +33,7 @@ pub fn build(b: *std.Build) !void { run_step.step.dependOn(&link_step.step); const run_option = b.step("run", "Run '$PROJECT_NAME'"); run_option.dependOn(&run_step.step); + return; } const exe = b.addExecutable(.{ .name = "'$PROJECT_NAME'", .root_source_file = .{ .path = "src/main.zig" }, .optimize = optimize, .target = target }); From 1c142efe55a776b4c5b93bb34b7de9c38b0edd18 Mon Sep 17 00:00:00 2001 From: Blue Date: Mon, 14 Aug 2023 00:03:25 -0600 Subject: [PATCH 16/19] fix project errors --- build.zig | 4 ++-- project_setup.sh | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build.zig b/build.zig index bb5ddb2..946e97e 100755 --- a/build.zig +++ b/build.zig @@ -221,7 +221,7 @@ pub fn emscriptenRunStep(b: *std.Build) !*std.Build.Step.Run { } //Creates the static library to build a project for Emscripten -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 { +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, @@ -249,7 +249,7 @@ fn compileForEmscripten(b: *std.Build, name: []const u8, root_source_file: []con // 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 way to convert from an input path to the output path, if emscripten even allows such a thing. // TODO: add a parameter that allows a custom output directory -fn linkWithEmscripten(b: *std.Build, itemsToLink: []const *std.Build.Step.Compile, filesToInclude: []const std.Build.LazyPath) !*std.Build.Step.Run { +pub fn linkWithEmscripten(b: *std.Build, itemsToLink: []const *std.Build.Step.Compile, filesToInclude: []const std.Build.LazyPath) !*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\"'"); diff --git a/project_setup.sh b/project_setup.sh index 4b6f37d..7e921a4 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -19,17 +19,17 @@ pub fn build(b: *std.Build) !void { var raylib_math = rl.math.getModule(b, "raylib-zig"); //web exports are completely separate if (target.getOsTag() == .emscripten) { - const exe_lib = compileForEmscripten(b, '$PROJECT_NAME', "src/main.zig", target, optimize); + const exe_lib = rl.compileForEmscripten(b, "'$PROJECT_NAME'", "src/main.zig", target, optimize); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); const raylib_artifact = rl.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. + // Note that raylib itself is not 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 }, &[_]std.Build.LazyPath{.{ .path = "resources/" }}); + const link_step = try rl.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }, &[_]std.Build.LazyPath{.{ .path = "resources/" }}); link_step.step.dependOn(&raylib_artifact.step); link_step.step.dependOn(&exe_lib.step); - b.getInstallStep.dependOn(&link_step.step); - const run_step = try emscriptenRunStep(b); + b.getInstallStep().dependOn(&link_step.step); + const run_step = try rl.emscriptenRunStep(b); run_step.step.dependOn(&link_step.step); const run_option = b.step("run", "Run '$PROJECT_NAME'"); run_option.dependOn(&run_step.step); From 7d62be63e2f578270d9ddbb6efc52b88719179cf Mon Sep 17 00:00:00 2001 From: Blue Date: Mon, 14 Aug 2023 00:09:46 -0600 Subject: [PATCH 17/19] small modification to emscripten build --- build.zig | 12 ++++-------- project_setup.sh | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/build.zig b/build.zig index 946e97e..5a32ef1 100755 --- a/build.zig +++ b/build.zig @@ -179,7 +179,9 @@ pub fn build(b: *std.Build) !void { 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 }, &[_]std.Build.LazyPath{.{ .path = "resources/" }}); + const link_step = try linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }); + link_step.addArg("--embed-file"); + link_step.addArg("resources/"); link_step.step.dependOn(&raylib_artifact.step); link_step.step.dependOn(&exe_lib.step); const run_step = try emscriptenRunStep(b); @@ -249,7 +251,7 @@ pub fn compileForEmscripten(b: *std.Build, name: []const u8, root_source_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 way to convert from an input path to the output path, if emscripten even allows such a thing. // TODO: add a parameter that allows a custom output directory -pub fn linkWithEmscripten(b: *std.Build, itemsToLink: []const *std.Build.Step.Compile, filesToInclude: []const std.Build.LazyPath) !*std.Build.Step.Run { +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\"'"); @@ -275,12 +277,6 @@ pub fn linkWithEmscripten(b: *std.Build, itemsToLink: []const *std.Build.Step.Co //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" }); - - //Web builds can't just have the files next to the executable, they have to be linked with the program - for (filesToInclude) |file| { - emcc_command.addArg("--embed-file"); - emcc_command.addArg(file.path); - } return emcc_command; } diff --git a/project_setup.sh b/project_setup.sh index 7e921a4..5b18cfd 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -25,7 +25,7 @@ pub fn build(b: *std.Build) !void { const raylib_artifact = rl.getArtifact(b, target, optimize); // Note that raylib itself is not 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 rl.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }, &[_]std.Build.LazyPath{.{ .path = "resources/" }}); + const link_step = try rl.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }); link_step.step.dependOn(&raylib_artifact.step); link_step.step.dependOn(&exe_lib.step); b.getInstallStep().dependOn(&link_step.step); From 7c6d8da1e9a15594efea88b778e6c3f3cb64824e Mon Sep 17 00:00:00 2001 From: Blue Date: Mon, 14 Aug 2023 00:21:47 -0600 Subject: [PATCH 18/19] remove unnesesary file from .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 5275153..118ccc7 100755 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ zig-cache/ zig-out/ .idea/ Project/* -#This is a file that is auto-generated to make a hack work -**/web_emscripten_entry_point.zig libraylib.a raylib.h raymath.h From 977d3057140a80c47f35904dc5ede87de6349949 Mon Sep 17 00:00:00 2001 From: Blue Date: Wed, 16 Aug 2023 13:06:02 -0600 Subject: [PATCH 19/19] some final changes --- build.zig | 4 +--- project_setup.sh | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/build.zig b/build.zig index 5a32ef1..debc5c7 100755 --- a/build.zig +++ b/build.zig @@ -182,8 +182,7 @@ pub fn build(b: *std.Build) !void { const link_step = try linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }); link_step.addArg("--embed-file"); link_step.addArg("resources/"); - link_step.step.dependOn(&raylib_artifact.step); - link_step.step.dependOn(&exe_lib.step); + const run_step = try emscriptenRunStep(b); run_step.step.dependOn(&link_step.step); const run_option = b.step(ex.name, ex.desc); @@ -249,7 +248,6 @@ pub fn compileForEmscripten(b: *std.Build, name: []const u8, root_source_file: [ // 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 way to convert from an input path to the output path, if emscripten even allows such a thing. // 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 diff --git a/project_setup.sh b/project_setup.sh index 5b18cfd..9bf1408 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -26,8 +26,6 @@ pub fn build(b: *std.Build) !void { // Note that raylib itself is not 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 rl.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }); - link_step.step.dependOn(&raylib_artifact.step); - link_step.step.dependOn(&exe_lib.step); b.getInstallStep().dependOn(&link_step.step); const run_step = try rl.emscriptenRunStep(b); run_step.step.dependOn(&link_step.step);