diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 15df466..472e61c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -48,6 +48,8 @@ jobs: run: cd template && zig build - name: Build Template Windows run: cd template && zig build -Dtarget=x86_64-windows + # - name: Build Template Web + # run: cd template && zig build -Dtarget=wasm32-emscripten test: name: Testing runs-on: ubuntu-latest diff --git a/build.zig b/build.zig index 22f54a7..bfa2c85 100644 --- a/build.zig +++ b/build.zig @@ -58,6 +58,20 @@ pub fn build(b: *std.Build) !void { "c_sdl_install_build_config_h", "Additionally install 'SDL_build_config.h' when installing SDL (default: false)", ) orelse false; + const sdl_system_include_path = b.option( + std.Build.LazyPath, + "sdl_system_include_path", + "System include path for SDL", + ); + const sdl_sysroot_path = b.option( + std.Build.LazyPath, + "sdl_sysroot_path", + "System include path for SDL", + ); + + if (sdl_sysroot_path) |val| { + b.sysroot = val.getPath(b); + } const sdl_dep = b.dependency("sdl", .{ .target = target, @@ -71,6 +85,8 @@ pub fn build(b: *std.Build) !void { }); const sdl_dep_lib = sdl_dep.artifact("SDL3"); + if (sdl_system_include_path) |val| + sdl_dep_lib.addSystemIncludePath(val); // SDL options. const extension_options = b.addOptions(); @@ -110,6 +126,8 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, }); translate_c.addIncludePath(sdl_dep.path("include")); + if (sdl_system_include_path) |val| + translate_c.addSystemIncludePath(val); const c_module = translate_c.createModule(); @@ -122,6 +140,9 @@ pub fn build(b: *std.Build) !void { }, }); + if (sdl_system_include_path) |val| + sdl3.addSystemIncludePath(val); + const example_options = ExampleOptions{ .ext_image = ext_image, .ext_net = ext_net, @@ -130,23 +151,24 @@ pub fn build(b: *std.Build) !void { sdl3.addOptions("extension_options", extension_options); sdl3.linkLibrary(sdl_dep_lib); + if (ext_image) { image.setup(b, sdl3, translate_c, sdl_dep_lib, c_sdl_preferred_linkage, .{ .optimize = optimize, .target = target, - }); + }, sdl_system_include_path); } if (ext_net) { net.setup(b, sdl3, translate_c, sdl_dep_lib, c_sdl_preferred_linkage, .{ .optimize = optimize, .target = target, - }); + }, sdl_system_include_path); } if (ext_ttf) { ttf.setup(b, sdl3, translate_c, sdl_dep_lib, c_sdl_preferred_linkage, .{ .optimize = optimize, .target = target, - }); + }, sdl_system_include_path); } _ = setupDocs(b, sdl3); diff --git a/build/image.zig b/build/image.zig index 1262b77..9cf2330 100644 --- a/build/image.zig +++ b/build/image.zig @@ -8,6 +8,7 @@ pub fn setup( sdl_dep_lib: *std.Build.Step.Compile, linkage: std.builtin.LinkMode, cfg: struct { optimize: std.builtin.OptimizeMode, target: std.Build.ResolvedTarget }, + system_include_path: ?std.Build.LazyPath, ) void { const upstream = b.lazyDependency("sdl_image", .{}) orelse return; @@ -22,6 +23,9 @@ pub fn setup( .link_libc = true, }), }); + if (system_include_path) |val| { + lib.root_module.addSystemIncludePath(val); + } lib.root_module.linkLibrary(sdl_dep_lib); // Use stb_image for loading JPEG and PNG files. Native alternatives such as diff --git a/build/net.zig b/build/net.zig index 3b0105a..c03355d 100644 --- a/build/net.zig +++ b/build/net.zig @@ -7,6 +7,7 @@ pub fn setup( sdl_dep_lib: *std.Build.Step.Compile, linkage: std.builtin.LinkMode, cfg: struct { optimize: std.builtin.OptimizeMode, target: std.Build.ResolvedTarget }, + system_include_path: ?std.Build.LazyPath, ) void { const target = cfg.target; const optimize = cfg.optimize; @@ -30,6 +31,10 @@ pub fn setup( .linkage = linkage, }); + if (system_include_path) |val| { + lib.addSystemIncludePath(val); + } + var lib_c_flags: std.ArrayListUnmanaged([]const u8) = .empty; defer lib_c_flags.deinit(b.allocator); lib_c_flags.appendSlice(b.allocator, &.{"-std=c99"}) catch @panic("OOM"); diff --git a/build/ttf.zig b/build/ttf.zig index 24e5d0d..c0a050c 100644 --- a/build/ttf.zig +++ b/build/ttf.zig @@ -8,6 +8,7 @@ pub fn setup( sdl_dep_lib: *std.Build.Step.Compile, linkage: std.builtin.LinkMode, cfg: struct { optimize: std.builtin.OptimizeMode, target: std.Build.ResolvedTarget }, + system_include_path: ?std.Build.LazyPath, ) void { const target = cfg.target; const optimize: std.builtin.OptimizeMode = .ReleaseFast; // https://github.com/libsdl-org/SDL_ttf/issues/566 (ReleaseFast prevents UBSAN from running) @@ -26,6 +27,10 @@ pub fn setup( }), }); + if (system_include_path) |val| { + lib.addSystemIncludePath(val); + } + translate_c.addIncludePath(upstream.path("include")); lib.addIncludePath(upstream.path("include")); lib.addIncludePath(upstream.path("src")); diff --git a/template/build.zig b/template/build.zig index 6fbb650..a9f307e 100644 --- a/template/build.zig +++ b/template/build.zig @@ -1,9 +1,7 @@ const std = @import("std"); +const zemscripten = @import("zemscripten"); -pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - +fn buildBin(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !void { const exe_mod = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, @@ -32,13 +30,116 @@ pub fn build(b: *std.Build) void { const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); +} - const exe_unit_tests = b.addTest(.{ - .root_module = exe_mod, +fn buildWeb(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !void { + const activateEmsdk = zemscripten.activateEmsdkStep(b); + b.default_step.dependOn(activateEmsdk); + + const wasm = b.addLibrary(.{ + .name = "template", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .link_libc = true, + }), + .linkage = .static, + }); + + const zemscripten_dep = b.dependency("zemscripten", .{}); + wasm.root_module.addImport("zemscripten", zemscripten_dep.module("root")); + + const emsdk_dep = b.dependency("emsdk", .{}); + const emsdk_sysroot_path = emsdk_dep.path("upstream/emscripten/cache/sysroot"); + const emsdk_sysroot_include_path = emsdk_dep.path("upstream/emscripten/cache/sysroot/include"); + + const sdl3 = b.dependency("sdl3", .{ + .target = target, + .optimize = optimize, + .callbacks = true, + .ext_image = true, + .sdl_system_include_path = emsdk_sysroot_include_path, + .sdl_sysroot_path = emsdk_sysroot_path, }); - const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + wasm.root_module.addSystemIncludePath(emsdk_sysroot_include_path); + + const sdl_module = sdl3.module("sdl3"); + sdl_module.addSystemIncludePath(emsdk_sysroot_include_path); + wasm.root_module.addImport("sdl3", sdl3.module("sdl3")); + wasm.addSystemIncludePath(emsdk_sysroot_include_path); - const test_step = b.step("test", "Run unit tests"); - test_step.dependOn(&run_exe_unit_tests.step); + const emcc_flags = zemscripten.emccDefaultFlags(b.allocator, .{ + .optimize = optimize, + .fsanitize = true, + }); + + var emcc_settings = zemscripten.emccDefaultSettings(b.allocator, .{ + .optimize = optimize, + }); + try emcc_settings.put("ALLOW_MEMORY_GROWTH", "1"); + emcc_settings.put("USE_SDL", "3") catch unreachable; + + const emcc_step = zemscripten.emccStep( + b, + wasm, + .{ + .optimize = optimize, + .flags = emcc_flags, // Pass the modified flags + .settings = emcc_settings, + .use_preload_plugins = true, + .embed_paths = &.{}, + .preload_paths = &.{}, + .install_dir = .{ .custom = "web" }, + .shell_file_path = b.path("src/html/shell.html"), + }, + ); + + b.getInstallStep().dependOn(emcc_step); + + const base_name = if (wasm.name_only_filename) |n| n else wasm.name; + + // Create filename with extension + const html_file = try std.fmt.allocPrint(b.allocator, "{s}.html", .{base_name}); + defer b.allocator.free(html_file); + + std.debug.print("HTML FILE: {s}\n", .{html_file}); + + // output set in emcc_step + const html_path = b.pathJoin(&.{ "zig-out", "web", html_file }); + + std.debug.print("HTML PATH: {s}\n", .{html_path}); + + // Absolute path to emrun + const emrun_path = emsdk_dep.path("upstream/emscripten/emrun"); + + // System command + const emrun_cmd = b.addSystemCommand(&.{ + emrun_path.getPath(b), + "--port", + b.fmt("{d}", .{b.option(u16, "port", "Port to run the webapp on (default: 8080)") orelse 8080}), + html_path, + }); + + emrun_cmd.step.dependOn(b.getInstallStep()); + + const run_step = b.step("run", "Run the app (via emrun)"); + run_step.dependOn(&emrun_cmd.step); + + if (b.args) |args| { + emrun_cmd.addArgs(args); + } +} + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const os = target.result.os.tag; + if (os == .emscripten) { + try buildWeb(b, target, optimize); + } else { + try buildBin(b, target, optimize); + } } diff --git a/template/build.zig.zon b/template/build.zig.zon index 74ad5dd..49bd420 100644 --- a/template/build.zig.zon +++ b/template/build.zig.zon @@ -2,11 +2,19 @@ .name = .sdl3_template, .version = "0.0.0", .dependencies = .{ + .emsdk = .{ + .url = "https://github.com/emscripten-core/emsdk/archive/refs/tags/4.0.3.tar.gz", + .hash = "N-V-__8AAOG3BQCJ9cn-N2swm2o5cLmDhmdHmtwNngOChK78", + }, .sdl3 = .{ // .url = "git+https://github.com/Gota7/zig-sdl3#v0.1.0", // .hash = "sdl3-0.0.1-NmT1Q5LaIAC1lVdodEX_pnLOI44bfRvW1ZuOlqrMY17E" .path = "../", }, + .zemscripten = .{ + .url = "git+https://github.com/zig-gamedev/zemscripten#00da03b188220374a57cb34cda6230b8d53737ea", + .hash = "zemscripten-0.2.0-dev-sRlDqFJSAAB8hgnRt5DDMKP3zLlDtMnUDwYRJVCa5lGY", + }, }, .paths = .{ "build.zig", diff --git a/template/src/html/shell.html b/template/src/html/shell.html new file mode 100644 index 0000000..ddfc3dc --- /dev/null +++ b/template/src/html/shell.html @@ -0,0 +1,34 @@ + + + + + + + + + + + {{{ SCRIPT }}} + + + + + diff --git a/template/src/main.zig b/template/src/main.zig index 6bbfd18..0ed4d96 100644 --- a/template/src/main.zig +++ b/template/src/main.zig @@ -1,5 +1,6 @@ const sdl3 = @import("sdl3"); const std = @import("std"); +const builtin = @import("builtin"); // Use main callbacks. comptime { @@ -18,7 +19,7 @@ pub const _start = void; pub const WinMainCRTStartup = void; /// Allocator we will use. -const allocator = std.heap.smp_allocator; +const allocator = if (builtin.os.tag != .emscripten) std.heap.smp_allocator else std.heap.c_allocator; /// For logging system messages. const log_app = sdl3.log.Category.application;