From 433c170c02a50a48b27a7541fc3b340ae70aaabe Mon Sep 17 00:00:00 2001 From: Not-Nik Date: Wed, 3 Apr 2024 19:37:10 +0200 Subject: [PATCH] Update project setup script to use raylib-zig as a dependency, instead of cloning the entire repo --- build.zig | 165 ++--------------------------------------------- emcc.zig | 159 +++++++++++++++++++++++++++++++++++++++++++++ project_setup.sh | 47 ++++++++++---- 3 files changed, 199 insertions(+), 172 deletions(-) create mode 100644 emcc.zig diff --git a/build.zig b/build.zig index f7b32d0..89e6cda 100755 --- a/build.zig +++ b/build.zig @@ -1,8 +1,8 @@ -// raylib-zig (c) Nikolas Wipper 2020-2023 +// raylib-zig (c) Nikolas Wipper 2020-2024 const std = @import("std"); const rl = @This(); -const builtin = @import("builtin"); +const emcc = @import("emcc.zig"); const Program = struct { name: []const u8, @@ -186,7 +186,7 @@ pub fn build(b: *std.Build) !void { for (examples) |ex| { if (target.getOsTag() == .emscripten) { - const exe_lib = compileForEmscripten(b, ex.name, ex.path, target, optimize); + const exe_lib = emcc.compileForEmscripten(b, ex.name, ex.path, target, optimize); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); const raylib_lib = getRaylib(b, target, optimize); @@ -194,11 +194,11 @@ pub fn build(b: *std.Build) !void { // 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_lib); - const link_step = try linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_lib }); + const link_step = try emcc.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_lib }); link_step.addArg("--embed-file"); link_step.addArg("resources/"); - const run_step = try emscriptenRunStep(b); + const run_step = try emcc.emscriptenRunStep(b); run_step.step.dependOn(&link_step.step); const run_option = b.step(ex.name, ex.desc); run_option.dependOn(&run_step.step); @@ -219,158 +219,3 @@ pub fn build(b: *std.Build) !void { } } } - -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 - \\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; -; diff --git a/emcc.zig b/emcc.zig new file mode 100644 index 0000000..406487f --- /dev/null +++ b/emcc.zig @@ -0,0 +1,159 @@ +// 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.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 + \\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; +; diff --git a/project_setup.sh b/project_setup.sh index 6f050de..5905227 100755 --- a/project_setup.sh +++ b/project_setup.sh @@ -10,24 +10,34 @@ mkdir "$PROJECT_NAME" && cd "$PROJECT_NAME" || exit touch build.zig echo "generating project files..." echo 'const std = @import("std"); -const rl = @import("raylib-zig/build.zig"); +const emcc = @import("emcc.zig"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const raylib = rl.getModule(b, "raylib-zig"); - const raylib_math = rl.math.getModule(b, "raylib-zig"); + + const raylib_dep = b.dependency("raylib-zig", .{ + .target = target, + .optimize = optimize, + }); + + const raylib = raylib_dep.module("raylib"); + const raylib_math = raylib_dep.module("raylib-math"); + const raylib_artifact = raylib_dep.artifact("raylib"); + //web exports are completely separate if (target.getOsTag() == .emscripten) { - const exe_lib = rl.compileForEmscripten(b, "'$PROJECT_NAME'", "src/main.zig", target, optimize); + const exe_lib = emcc.compileForEmscripten(b, "'$PROJECT_NAME'", "src/main.zig", target, optimize); + + exe_lib.linkLibrary(raylib_artifact); exe_lib.addModule("raylib", raylib); exe_lib.addModule("raylib-math", raylib_math); - const raylib_artifact = rl.getRaylib(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 }); + const link_step = try emcc.linkWithEmscripten(b, &[_]*std.Build.Step.Compile{ exe_lib, raylib_artifact }); + b.getInstallStep().dependOn(&link_step.step); - const run_step = try rl.emscriptenRunStep(b); + const run_step = try emcc.emscriptenRunStep(b); run_step.step.dependOn(&link_step.step); const run_option = b.step("run", "Run '$PROJECT_NAME'"); run_option.dependOn(&run_step.step); @@ -36,7 +46,7 @@ pub fn build(b: *std.Build) !void { 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.linkLibrary(raylib_artifact); exe.addModule("raylib", raylib); exe.addModule("raylib-math", raylib_math); @@ -48,8 +58,21 @@ pub fn build(b: *std.Build) !void { } ' >> build.zig +echo '.{ + .name = "'$PROJECT_NAME'", + .version = "0.0.1", + .dependencies = .{ + .@"raylib-zig" = .{ + .url = "https://github.com/Not-Nik/raylib-zig/archive/d002b702c8fb2c1c7fd4508addae50ffe27620cd.tar.gz", + .hash = "1220d18ffceb49556360746f7f269ebb280f2d9b0f36f6d22e282426477f27056889", + }, + }, + .paths = .{""}, +} + +' >> build.zig.zon + +cp ../emcc.zig . + mkdir src cp ../examples/core/basic_window.zig src/main.zig -cp ../build.zig.zon . -echo "cloning raylib-zig inside of project..." -git clone ../ raylib-zig --recursive