From 0d29d8d4b18c5cf3a3f1320b1a6c8870e0f1b0c3 Mon Sep 17 00:00:00 2001 From: HaxSam Date: Sun, 31 Aug 2025 11:32:19 +0200 Subject: [PATCH] [build.zig]: a concrete approach to build for web with zig-build (#5157) --- build.zig | 462 +++++++++++++++++++++++--------------------------- build.zig.zon | 7 +- 2 files changed, 219 insertions(+), 250 deletions(-) diff --git a/build.zig b/build.zig index f82ccc531..8bbc8073f 100644 --- a/build.zig +++ b/build.zig @@ -14,58 +14,80 @@ comptime { @compileError("Raylib requires zig version " ++ min_ver); } -fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend) void { - switch (platform) { - .glfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_GLFW", ""), - .rgfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_RGFW", ""), - .sdl => raylib.root_module.addCMacro("PLATFORM_DESKTOP_SDL", ""), - .android => raylib.root_module.addCMacro("PLATFORM_ANDROID", ""), - else => {}, - } -} +pub const emsdk = struct { + const zemscripten = @import("zemscripten"); -fn createEmsdkStep(b: *std.Build, emsdk: *std.Build.Dependency) *std.Build.Step.Run { - if (builtin.os.tag == .windows) { - return b.addSystemCommand(&.{emsdk.path("emsdk.bat").getPath(b)}); - } else { - return b.addSystemCommand(&.{emsdk.path("emsdk").getPath(b)}); + pub fn shell(b: *std.Build) std.Build.LazyPath { + return b.dependency("raylib", .{}).path("src/shell.html"); } -} -fn emSdkSetupStep(b: *std.Build, emsdk: *std.Build.Dependency) !?*std.Build.Step.Run { - const dot_emsc_path = emsdk.path(".emscripten").getPath(b); - const dot_emsc_exists = !std.meta.isError(std.fs.accessAbsolute(dot_emsc_path, .{})); - - if (!dot_emsc_exists) { - const emsdk_install = createEmsdkStep(b, emsdk); - emsdk_install.addArgs(&.{ "install", "latest" }); - const emsdk_activate = createEmsdkStep(b, emsdk); - emsdk_activate.addArgs(&.{ "activate", "latest" }); - emsdk_activate.step.dependOn(&emsdk_install.step); - return emsdk_activate; - } else { - return null; + pub const FlagsOptions = struct { + optimize: std.builtin.OptimizeMode, + asyncify: bool = true, + }; + + pub fn emccDefaultFlags(allocator: std.mem.Allocator, options: FlagsOptions) zemscripten.EmccFlags { + var emcc_flags = zemscripten.emccDefaultFlags(allocator, .{ + .optimize = options.optimize, + .fsanitize = true, + }); + + if (options.asyncify) + emcc_flags.put("-sASYNCIFY", {}) catch unreachable; + + return emcc_flags; } -} -// Adapted from Not-Nik/raylib-zig -fn emscriptenRunStep(b: *std.Build, emsdk: *std.Build.Dependency, examplePath: []const u8) !*std.Build.Step.Run { - const dot_emsc_path = emsdk.path("upstream/emscripten/cache/sysroot/include").getPath(b); - // If compiling on windows , use emrun.bat. - const emrunExe = switch (builtin.os.tag) { - .windows => "emrun.bat", - else => "emrun", + pub const SettingsOptions = struct { + optimize: std.builtin.OptimizeMode, + es3: bool = true, + emsdk_allocator: zemscripten.EmsdkAllocator = .emmalloc, }; - var emrun_run_arg = try b.allocator.alloc(u8, dot_emsc_path.len + emrunExe.len + 1); - defer b.allocator.free(emrun_run_arg); - if (b.sysroot == null) { - emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ emsdk.path("upstream/emscripten").getPath(b), emrunExe }); - } else { - emrun_run_arg = try std.fmt.bufPrint(emrun_run_arg, "{s}" ++ std.fs.path.sep_str ++ "{s}", .{ dot_emsc_path, emrunExe }); + pub fn emccDefaultSettings(allocator: std.mem.Allocator, options: SettingsOptions) zemscripten.EmccSettings { + var emcc_settings = zemscripten.emccDefaultSettings(allocator, .{ + .optimize = options.optimize, + .emsdk_allocator = options.emsdk_allocator, + }); + + if (options.es3) + emcc_settings.put("FULL_ES3", "1") catch unreachable; + emcc_settings.put("USE_GLFW", "3") catch unreachable; + emcc_settings.put("EXPORTED_RUNTIME_METHODS", "['requestFullscreen']") catch unreachable; + + return emcc_settings; + } + + pub fn emccStep(b: *std.Build, raylib: *std.Build.Step.Compile, wasm: *std.Build.Step.Compile, options: zemscripten.StepOptions) *std.Build.Step { + const activate_emsdk_step = zemscripten.activateEmsdkStep(b); + + const emsdk_dep = b.dependency("emsdk", .{}); + raylib.root_module.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include")); + wasm.root_module.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include")); + + const emcc_step = zemscripten.emccStep(b, wasm, options); + emcc_step.dependOn(activate_emsdk_step); + + return emcc_step; + } + + pub fn emrunStep( + b: *std.Build, + html_path: []const u8, + extra_args: []const []const u8, + ) *std.Build.Step { + return zemscripten.emrunStep(b, html_path, extra_args); + } +}; + +fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend) void { + switch (platform) { + .glfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_GLFW", ""), + .rgfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_RGFW", ""), + .sdl => raylib.root_module.addCMacro("PLATFORM_DESKTOP_SDL", ""), + .android => raylib.root_module.addCMacro("PLATFORM_ANDROID", ""), + else => {}, } - const run_cmd = b.addSystemCommand(&.{ emrun_run_arg, examplePath }); - return run_cmd; } /// A list of all flags from `src/config.h` that one may override @@ -99,10 +121,20 @@ const config_h_flags = outer: { break :outer flags[0..i].*; }; -pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, options: Options) !*std.Build.Step.Compile { +fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, options: Options) !*std.Build.Step.Compile { var raylib_flags_arr: std.ArrayList([]const u8) = .empty; defer raylib_flags_arr.deinit(b.allocator); + const raylib = b.addLibrary(.{ + .name = "raylib", + .linkage = options.linkage, + .root_module = b.createModule(.{ + .optimize = optimize, + .target = target, + .link_libc = true, + }), + }); + try raylib_flags_arr.appendSlice( b.allocator, &[_][]const u8{ @@ -113,7 +145,7 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: }, ); - if (options.shared) { + if (options.linkage == .dynamic) { try raylib_flags_arr.appendSlice( b.allocator, &[_][]const u8{ @@ -159,16 +191,6 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: try raylib_flags_arr.appendSlice(b.allocator, &config_h_flags); } - const raylib = b.addLibrary(.{ - .name = "raylib", - .linkage = if (options.shared) .dynamic else .static, - .root_module = b.createModule(.{ - .target = target, - .optimize = optimize, - }), - }); - raylib.linkLibC(); - // No GLFW required on PLATFORM_DRM if (options.platform != .drm) { raylib.addIncludePath(b.path("src/external/glfw/include")); @@ -210,22 +232,22 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: .rgfw, .sdl, .drm, .android => {}, } - raylib.linkSystemLibrary("winmm"); - raylib.linkSystemLibrary("gdi32"); - raylib.linkSystemLibrary("opengl32"); + raylib.root_module.linkSystemLibrary("winmm", .{}); + raylib.root_module.linkSystemLibrary("gdi32", .{}); + raylib.root_module.linkSystemLibrary("opengl32", .{}); setDesktopPlatform(raylib, options.platform); }, .linux => { if (options.platform == .drm) { if (options.opengl_version == .auto) { - raylib.linkSystemLibrary("GLESv2"); + raylib.root_module.linkSystemLibrary("GLESv2", .{}); raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); } - raylib.linkSystemLibrary("EGL"); - raylib.linkSystemLibrary("gbm"); - raylib.linkSystemLibrary2("libdrm", .{ .use_pkg_config = .force }); + raylib.root_module.linkSystemLibrary("EGL", .{}); + raylib.root_module.linkSystemLibrary("gbm", .{}); + raylib.root_module.linkSystemLibrary("libdrm", .{ .use_pkg_config = .force }); raylib.root_module.addCMacro("PLATFORM_DRM", ""); raylib.root_module.addCMacro("EGL_NO_X11", ""); @@ -260,12 +282,12 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: const androidAsmPath = try std.fs.path.join(b.allocator, &.{ androidIncludePath, "/asm-generic" }); const androidGluePath = try std.fs.path.join(b.allocator, &.{ androidNdkPathString, "/sources/android/native_app_glue/" }); - raylib.addLibraryPath(.{ .cwd_relative = androidLibPath }); + raylib.root_module.addLibraryPath(.{ .cwd_relative = androidLibPath }); raylib.root_module.addLibraryPath(.{ .cwd_relative = androidApiSpecificPath }); - raylib.addSystemIncludePath(.{ .cwd_relative = androidIncludePath }); - raylib.addSystemIncludePath(.{ .cwd_relative = androidArchIncludePath }); - raylib.addSystemIncludePath(.{ .cwd_relative = androidAsmPath }); - raylib.addSystemIncludePath(.{ .cwd_relative = androidGluePath }); + raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidIncludePath }); + raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidArchIncludePath }); + raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidAsmPath }); + raylib.root_module.addSystemIncludePath(.{ .cwd_relative = androidGluePath }); var libcData: std.ArrayList(u8) = .empty; var aw: std.Io.Writer.Allocating = .fromArrayList(b.allocator, &libcData); @@ -289,15 +311,15 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: if (options.linux_display_backend == .X11 or options.linux_display_backend == .Both) { raylib.root_module.addCMacro("_GLFW_X11", ""); - raylib.linkSystemLibrary("GLX"); - raylib.linkSystemLibrary("X11"); - raylib.linkSystemLibrary("Xcursor"); - raylib.linkSystemLibrary("Xext"); - raylib.linkSystemLibrary("Xfixes"); - raylib.linkSystemLibrary("Xi"); - raylib.linkSystemLibrary("Xinerama"); - raylib.linkSystemLibrary("Xrandr"); - raylib.linkSystemLibrary("Xrender"); + raylib.root_module.linkSystemLibrary("GLX", .{}); + raylib.root_module.linkSystemLibrary("X11", .{}); + raylib.root_module.linkSystemLibrary("Xcursor", .{}); + raylib.root_module.linkSystemLibrary("Xext", .{}); + raylib.root_module.linkSystemLibrary("Xfixes", .{}); + raylib.root_module.linkSystemLibrary("Xi", .{}); + raylib.root_module.linkSystemLibrary("Xinerama", .{}); + raylib.root_module.linkSystemLibrary("Xrandr", .{}); + raylib.root_module.linkSystemLibrary("Xrender", .{}); } if (options.linux_display_backend == .Wayland or options.linux_display_backend == .Both) { @@ -309,9 +331,9 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: @panic("`wayland-scanner` not found"); }; raylib.root_module.addCMacro("_GLFW_WAYLAND", ""); - raylib.linkSystemLibrary("EGL"); - raylib.linkSystemLibrary("wayland-client"); - raylib.linkSystemLibrary("xkbcommon"); + raylib.root_module.linkSystemLibrary("EGL", .{}); + raylib.root_module.linkSystemLibrary("wayland-client", .{}); + raylib.root_module.linkSystemLibrary("xkbcommon", .{}); waylandGenerate(b, raylib, "wayland.xml", "wayland-client-protocol"); waylandGenerate(b, raylib, "xdg-shell.xml", "xdg-shell-client-protocol"); waylandGenerate(b, raylib, "xdg-decoration-unstable-v1.xml", "xdg-decoration-unstable-v1-client-protocol"); @@ -327,25 +349,25 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: }, .freebsd, .openbsd, .netbsd, .dragonfly => { try c_source_files.append(b.allocator, "rglfw.c"); - raylib.linkSystemLibrary("GL"); - raylib.linkSystemLibrary("rt"); - raylib.linkSystemLibrary("dl"); - raylib.linkSystemLibrary("m"); - raylib.linkSystemLibrary("X11"); - raylib.linkSystemLibrary("Xrandr"); - raylib.linkSystemLibrary("Xinerama"); - raylib.linkSystemLibrary("Xi"); - raylib.linkSystemLibrary("Xxf86vm"); - raylib.linkSystemLibrary("Xcursor"); + raylib.root_module.linkSystemLibrary("GL", .{}); + raylib.root_module.linkSystemLibrary("rt", .{}); + raylib.root_module.linkSystemLibrary("dl", .{}); + raylib.root_module.linkSystemLibrary("m", .{}); + raylib.root_module.linkSystemLibrary("X11", .{}); + raylib.root_module.linkSystemLibrary("Xrandr", .{}); + raylib.root_module.linkSystemLibrary("Xinerama", .{}); + raylib.root_module.linkSystemLibrary("Xi", .{}); + raylib.root_module.linkSystemLibrary("Xxf86vm", .{}); + raylib.root_module.linkSystemLibrary("Xcursor", .{}); setDesktopPlatform(raylib, options.platform); }, .macos => { // Include xcode_frameworks for cross compilation if (b.lazyDependency("xcode_frameworks", .{})) |dep| { - raylib.addSystemFrameworkPath(dep.path("Frameworks")); - raylib.addSystemIncludePath(dep.path("include")); - raylib.addLibraryPath(dep.path("lib")); + raylib.root_module.addSystemFrameworkPath(dep.path("Frameworks")); + raylib.root_module.addSystemIncludePath(dep.path("include")); + raylib.root_module.addLibraryPath(dep.path("lib")); } // On macos rglfw.c include Objective-C files. @@ -355,26 +377,18 @@ pub fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: .flags = raylib_flags_arr.items, }); _ = raylib_flags_arr.pop(); - raylib.linkFramework("Foundation"); - raylib.linkFramework("CoreServices"); - raylib.linkFramework("CoreGraphics"); - raylib.linkFramework("AppKit"); - raylib.linkFramework("IOKit"); + raylib.root_module.linkFramework("Foundation", .{}); + raylib.root_module.linkFramework("CoreServices", .{}); + raylib.root_module.linkFramework("CoreGraphics", .{}); + raylib.root_module.linkFramework("AppKit", .{}); + raylib.root_module.linkFramework("IOKit", .{}); setDesktopPlatform(raylib, options.platform); }, .emscripten => { - if (b.lazyDependency("emsdk", .{})) |dep| { - if (try emSdkSetupStep(b, dep)) |emSdkStep| { - raylib.step.dependOn(&emSdkStep.step); - } - - raylib.addIncludePath(dep.path("upstream/emscripten/cache/sysroot/include")); - } - raylib.root_module.addCMacro("PLATFORM_WEB", ""); if (options.opengl_version == .auto) { - raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES2", ""); + raylib.root_module.addCMacro("GRAPHICS_API_OPENGL_ES3", ""); } }, else => { @@ -396,9 +410,9 @@ pub fn addRaygui(b: *std.Build, raylib: *std.Build.Step.Compile, raygui_dep: *st raylib.step.dependOn(&gen_step.step); const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.addCSourceFile(.{ .file = raygui_c_path }); - raylib.addIncludePath(raygui_dep.path("src")); - raylib.addIncludePath(raylib_dep.path("src")); + raylib.root_module.addCSourceFile(.{ .file = raygui_c_path }); + raylib.root_module.addIncludePath(raygui_dep.path("src")); + raylib.root_module.addIncludePath(raylib_dep.path("src")); raylib.installHeader(raygui_dep.path("src/raygui.h"), "raygui.h"); } @@ -410,7 +424,7 @@ pub const Options = struct { rtext: bool = true, rtextures: bool = true, platform: PlatformBackend = .glfw, - shared: bool = false, + linkage: std.builtin.LinkMode = .static, linux_display_backend: LinuxDisplayBackend = .Both, opengl_version: OpenglVersion = .auto, android_ndk: []const u8 = "", @@ -428,7 +442,7 @@ pub const Options = struct { .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext, .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures, .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes, - .shared = b.option(bool, "shared", "Compile as shared library") orelse defaults.shared, + .linkage = b.option(std.builtin.LinkMode, "linkage", "Compile as shared or static library") orelse defaults.linkage, .linux_display_backend = b.option(LinuxDisplayBackend, "linux_display_backend", "Linux display backend to use") orelse defaults.linux_display_backend, .opengl_version = b.option(OpenglVersion, "opengl_version", "OpenGL version to use") orelse defaults.opengl_version, .config = b.option([]const u8, "config", "Compile with custom define macros overriding config.h") orelse &.{}, @@ -475,14 +489,7 @@ pub const PlatformBackend = enum { }; pub fn build(b: *std.Build) !void { - // Standard target options allows the person running `zig build` to choose - // what target to build for. Here we do not override the defaults, which - // means any target is allowed, and the default is native. Other options - // for restricting supported target set are available. const target = b.standardTargetOptions(.{}); - // Standard optimization options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not - // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); const lib = try compileRaylib(b, target, optimize, Options.getOptions(b)); @@ -505,29 +512,6 @@ pub fn build(b: *std.Build) !void { examples.dependOn(try addExamples("textures", b, target, optimize, lib)); } -fn waylandGenerate( - b: *std.Build, - raylib: *std.Build.Step.Compile, - comptime protocol: []const u8, - comptime basename: []const u8, -) void { - const waylandDir = "src/external/glfw/deps/wayland"; - const protocolDir = b.pathJoin(&.{ waylandDir, protocol }); - const clientHeader = basename ++ ".h"; - const privateCode = basename ++ "-code.h"; - - const client_step = b.addSystemCommand(&.{ "wayland-scanner", "client-header" }); - client_step.addFileArg(b.path(protocolDir)); - raylib.addIncludePath(client_step.addOutputFileArg(clientHeader).dirname()); - - const private_step = b.addSystemCommand(&.{ "wayland-scanner", "private-code" }); - private_step.addFileArg(b.path(protocolDir)); - raylib.addIncludePath(private_step.addOutputFileArg(privateCode).dirname()); - - raylib.step.dependOn(&client_step.step); - raylib.step.dependOn(&private_step.step); -} - fn addExamples( comptime module: []const u8, b: *std.Build, @@ -537,9 +521,8 @@ fn addExamples( ) !*std.Build.Step { const all = b.step(module, "All " ++ module ++ " examples"); const module_subpath = b.pathJoin(&.{ "examples", module }); - const module_resources = b.pathJoin(&.{ module_subpath, "resources@resources" }); var dir = try std.fs.cwd().openDir(b.pathFromRoot(module_subpath), .{ .iterate = true }); - defer if (comptime builtin.zig_version.minor >= 12) dir.close(); + defer dir.close(); var iter = dir.iterate(); while (try iter.next()) |entry| { @@ -551,20 +534,21 @@ fn addExamples( // zig's mingw headers do not include pthread.h if (std.mem.eql(u8, "core_loading_thread", name) and target.result.os.tag == .windows) continue; + const exe_mod = b.createModule(.{ + .target = target, + .optimize = optimize, + }); + exe_mod.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); + exe_mod.linkLibrary(raylib); + + const run_step = b.step(name, name); + if (target.result.os.tag == .emscripten) { - const exe_lib = b.addLibrary(.{ + const wasm = b.addLibrary(.{ .name = name, .linkage = .static, - .root_module = b.createModule(.{ - .target = target, - .optimize = optimize, - }), + .root_module = exe_mod, }); - exe_lib.addCSourceFile(.{ - .file = b.path(path), - .flags = &.{}, - }); - exe_lib.linkLibC(); if (std.mem.eql(u8, name, "rlgl_standalone")) { //TODO: Make rlgl_standalone example work @@ -575,139 +559,121 @@ fn addExamples( continue; } - exe_lib.linkLibrary(raylib); - - // Include emscripten for cross compilation - if (b.lazyDependency("emsdk", .{})) |emsdk_dep| { - if (try emSdkSetupStep(b, emsdk_dep)) |emSdkStep| { - exe_lib.step.dependOn(&emSdkStep.step); - } - - exe_lib.addIncludePath(emsdk_dep.path("upstream/emscripten/cache/sysroot/include")); - - // Create the output directory because emcc can't do it. - const emccOutputDirExample = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str }); - const mkdir_command = switch (builtin.os.tag) { - .windows => b.addSystemCommand(&.{ "cmd.exe", "/c", "if", "not", "exist", emccOutputDirExample, "mkdir", emccOutputDirExample }), - else => b.addSystemCommand(&.{ "mkdir", "-p", emccOutputDirExample }), - }; - - const emcc_exe = switch (builtin.os.tag) { - .windows => "emcc.bat", - else => "emcc", - }; - - const emcc_exe_path = b.pathJoin(&.{ emsdk_dep.path("upstream/emscripten").getPath(b), emcc_exe }); - const emcc_command = b.addSystemCommand(&[_][]const u8{emcc_exe_path}); - emcc_command.step.dependOn(&mkdir_command.step); - const emccOutputDirExampleWithFile = b.pathJoin(&.{ emccOutputDir, name, std.fs.path.sep_str, emccOutputFile }); - emcc_command.addArgs(&[_][]const u8{ - "-o", - emccOutputDirExampleWithFile, - "-sFULL-ES3=1", - "-sUSE_GLFW=3", - "-sSTACK_OVERFLOW_CHECK=1", - "-sEXPORTED_RUNTIME_METHODS=['requestFullscreen']", - "-sASYNCIFY", - "-O0", - "--emrun", - "--preload-file", - module_resources, - "--shell-file", - b.path("src/shell.html").getPath(b), - }); - - const link_items: []const *std.Build.Step.Compile = &.{ - raylib, - exe_lib, - }; - for (link_items) |item| { - emcc_command.addFileArg(item.getEmittedBin()); - emcc_command.step.dependOn(&item.step); - } - - const run_step = try emscriptenRunStep(b, emsdk_dep, emccOutputDirExampleWithFile); - run_step.step.dependOn(&emcc_command.step); - run_step.addArg("--no_browser"); - const run_option = b.step(name, name); + const emcc_flags = emsdk.emccDefaultFlags(b.allocator, .{ .optimize = optimize }); + const emcc_settings = emsdk.emccDefaultSettings(b.allocator, .{ .optimize = optimize }); + + const install_dir: std.Build.InstallDir = .{ .custom = "htmlout" }; + const emcc_step = emsdk.emccStep(b, raylib, wasm, .{ + .optimize = optimize, + .flags = emcc_flags, + .settings = emcc_settings, + .shell_file_path = b.path("src/shell.html"), + .embed_paths = &.{ + .{ + .src_path = b.pathJoin(&.{ module_subpath, "resources" }), + .virtual_path = "resources", + }, + }, + .install_dir = install_dir, + }); - run_option.dependOn(&run_step.step); + const html_filename = try std.fmt.allocPrint(b.allocator, "{s}.html", .{wasm.name}); + const emrun_step = emsdk.emrunStep( + b, + b.getInstallPath(install_dir, html_filename), + &.{"--no_browser"}, + ); + emrun_step.dependOn(emcc_step); - all.dependOn(&emcc_command.step); - } + run_step.dependOn(emrun_step); + all.dependOn(emcc_step); } else { - const exe = b.addExecutable(.{ - .name = name, - .root_module = b.createModule(.{ - .target = target, - .optimize = optimize, - }), - }); - exe.addCSourceFile(.{ .file = b.path(path), .flags = &.{} }); - exe.linkLibC(); - // special examples that test using these external dependencies directly // alongside raylib if (std.mem.eql(u8, name, "rlgl_standalone")) { - exe.addIncludePath(b.path("src")); - exe.addIncludePath(b.path("src/external/glfw/include")); + exe_mod.addIncludePath(b.path("src")); + exe_mod.addIncludePath(b.path("src/external/glfw/include")); if (!hasCSource(raylib.root_module, "rglfw.c")) { - exe.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} }); + exe_mod.addCSourceFile(.{ .file = b.path("src/rglfw.c"), .flags = &.{} }); } } if (std.mem.eql(u8, name, "raylib_opengl_interop")) { - exe.addIncludePath(b.path("src/external")); + exe_mod.addIncludePath(b.path("src/external")); } - exe.linkLibrary(raylib); - switch (target.result.os.tag) { .windows => { - exe.linkSystemLibrary("winmm"); - exe.linkSystemLibrary("gdi32"); - exe.linkSystemLibrary("opengl32"); + exe_mod.linkSystemLibrary("winmm", .{}); + exe_mod.linkSystemLibrary("gdi32", .{}); + exe_mod.linkSystemLibrary("opengl32", .{}); - exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); + exe_mod.addCMacro("PLATFORM_DESKTOP", ""); }, .linux => { - exe.linkSystemLibrary("GL"); - exe.linkSystemLibrary("rt"); - exe.linkSystemLibrary("dl"); - exe.linkSystemLibrary("m"); - exe.linkSystemLibrary("X11"); + exe_mod.linkSystemLibrary("GL", .{}); + exe_mod.linkSystemLibrary("rt", .{}); + exe_mod.linkSystemLibrary("dl", .{}); + exe_mod.linkSystemLibrary("m", .{}); + exe_mod.linkSystemLibrary("X11", .{}); - exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); + exe_mod.addCMacro("PLATFORM_DESKTOP", ""); }, .macos => { - exe.linkFramework("Foundation"); - exe.linkFramework("Cocoa"); - exe.linkFramework("OpenGL"); - exe.linkFramework("CoreAudio"); - exe.linkFramework("CoreVideo"); - exe.linkFramework("IOKit"); - - exe.root_module.addCMacro("PLATFORM_DESKTOP", ""); + exe_mod.linkFramework("Foundation", .{}); + exe_mod.linkFramework("Cocoa", .{}); + exe_mod.linkFramework("OpenGL", .{}); + exe_mod.linkFramework("CoreAudio", .{}); + exe_mod.linkFramework("CoreVideo", .{}); + exe_mod.linkFramework("IOKit", .{}); + + exe_mod.addCMacro("PLATFORM_DESKTOP", ""); }, else => { @panic("Unsupported OS"); }, } + const exe = b.addExecutable(.{ + .name = name, + .root_module = exe_mod, + }); + const install_cmd = b.addInstallArtifact(exe, .{}); const run_cmd = b.addRunArtifact(exe); run_cmd.cwd = b.path(module_subpath); run_cmd.step.dependOn(&install_cmd.step); - const run_step = b.step(name, name); run_step.dependOn(&run_cmd.step); - all.dependOn(&install_cmd.step); } } return all; } +fn waylandGenerate( + b: *std.Build, + raylib: *std.Build.Step.Compile, + comptime protocol: []const u8, + comptime basename: []const u8, +) void { + const waylandDir = "src/external/glfw/deps/wayland"; + const protocolDir = b.pathJoin(&.{ waylandDir, protocol }); + const clientHeader = basename ++ ".h"; + const privateCode = basename ++ "-code.h"; + + const client_step = b.addSystemCommand(&.{ "wayland-scanner", "client-header" }); + client_step.addFileArg(b.path(protocolDir)); + raylib.root_module.addIncludePath(client_step.addOutputFileArg(clientHeader).dirname()); + + const private_step = b.addSystemCommand(&.{ "wayland-scanner", "private-code" }); + private_step.addFileArg(b.path(protocolDir)); + raylib.root_module.addIncludePath(private_step.addOutputFileArg(privateCode).dirname()); + + raylib.step.dependOn(&client_step.step); + raylib.step.dependOn(&private_step.step); +} + fn hasCSource(module: *std.Build.Module, name: []const u8) bool { for (module.link_objects.items) |o| switch (o) { .c_source_file => |c| if (switch (c.file) { diff --git a/build.zig.zon b/build.zig.zon index 7037008ed..dbbde3aad 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .raylib, - .version = "5.5.0", + .version = "5.6.0-dev", .minimum_zig_version = "0.15.1", .fingerprint = 0x13035e5cb8bc1ac2, // Changing this has security and trust implications. @@ -14,7 +14,10 @@ .emsdk = .{ .url = "git+https://github.com/emscripten-core/emsdk#4.0.9", .hash = "N-V-__8AAJl1DwBezhYo_VE6f53mPVm00R-Fk28NPW7P14EQ", - .lazy = true, + }, + .zemscripten = .{ + .url = "git+https://github.com/zig-gamedev/zemscripten#3fa4b778852226c7346bdcc3c1486e875a9a6d02", + .hash = "zemscripten-0.2.0-dev-sRlDqApRAACspTbAZnuNKWIzfWzSYgYkb2nWAXZ-tqqt", }, },