diff --git a/build.zig b/build.zig index 1f4840a..e7f8689 100644 --- a/build.zig +++ b/build.zig @@ -2,7 +2,8 @@ const std = @import("std"); const Build = std.Build; const sokol = @import("sokol"); -const game_name = "sokol_template_v2"; +const game_name = "sokoban"; +const itchio_target = "SlimeBOOS/sokoban"; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); @@ -16,14 +17,20 @@ pub fn build(b: *std.Build) !void { const pkg_install_dir: Build.InstallDir = .{ .custom = "pkg" }; - var run_cmd_step: *std.Build.Step = undefined; - switch (try buildProject(ctx, target, optimize)) { + const run_step = b.step("run", "Run game"); + + const artifact = try buildProject(ctx, target, optimize, .{ + .imgui = b.option(bool, "imgui", "ImGui integration"), + .assets_from_filesystem = b.option(bool, "assets-filesystem", "Allow reading assets from filesystem"), + .tracy = b.option(bool, "tracy", "Tracy integration") + }); + switch (artifact) { .native => |exe| { b.installArtifact(exe); const run_cmd = b.addRunArtifact(exe); run_cmd.addPassthruArgs(); - run_cmd_step = &run_cmd.step; + run_step.dependOn(&run_cmd.step); { const exe_tests = b.addTest(.{ .root_module = exe.root_module, }); @@ -36,35 +43,98 @@ pub fn build(b: *std.Build) !void { .wasm => |install_dir| { b.getInstallStep().dependOn(&install_dir.step); - var create_zip_run = b.addRunArtifact(ctx.create_zip); - create_zip_run.addDirectoryArg(install_dir.options.source_dir); - const wasm_zip = create_zip_run.addOutputFileArg("html.zip"); - var html_install_file = b.addInstallFileWithDir(wasm_zip, pkg_install_dir, b.fmt("{s}_html.zip", .{game_name})); - b.getInstallStep().dependOn(&html_install_file.step); - const dep_sokol = b.dependency("sokol", .{}); const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{}); const emrun_step = sokol.emRunStep(b, .{ .name = "index", .emsdk = dep_emsdk }); emrun_step.step.dependOn(&install_dir.step); - run_cmd_step = &emrun_step.step; + run_step.dependOn(&emrun_step.step); - // add an emsdk install step const emsdk_install_step = sokol.emSdkInstallStep(b, dep_sokol.builder.dependency("emsdk", .{}), .{}); b.step("install-emsdk", "Install Emscripten SDK in zig-pkg").dependOn(emsdk_install_step); } } - const run_step = b.step("run", "Run game"); - run_step.dependOn(run_cmd_step); + { + const package_suffix = getPackageSuffix(target); + const package_name = b.fmt("{s}_{s}.zip", .{game_name, package_suffix}); + const package = artifact.createPackage(ctx, package_name); + + const package_step = b.step("package", "Pack artifacts into package"); + package_step.dependOn(&b.addInstallFileWithDir(package, pkg_install_dir, package_name).step); + + const deploy_step = b.step("deploy", "Deploy package to itch.io"); + const deploy_run = butlerPush(b, package, package_suffix); + deploy_step.dependOn(&deploy_run.step); + } + + { + const deploy_all_step = b.step("deploy-all", "Deploy windows,linux and web to itch.io"); + inline for (.{ + b.graph.host, // TODO: Add proper target for linux. Encountered problems with zig complaining about dynamic libraries. + b.resolveTargetQuery(.{ + .os_tag = .windows, + .cpu_arch = .x86_64 + }), + b.resolveTargetQuery(.{ + .os_tag = .emscripten, + .cpu_arch = .wasm32 + }), + }) |deploy_target| { + const deploy_artifact = try buildProject(ctx, deploy_target, .ReleaseSmall, .{}); + const package_suffix = getPackageSuffix(deploy_target); + const package_name = b.fmt("{s}_{s}.zip", .{game_name, package_suffix}); + const package = deploy_artifact.createPackage(ctx, package_name); + const deploy_run = butlerPush(b, package, package_suffix); + deploy_all_step.dependOn(&deploy_run.step); + } + } +} + +fn butlerPush(b: *Build, package: Build.LazyPath, channel: []const u8) *Build.Step.Run { + const deploy_run = b.addSystemCommand(&.{"butler", "push"}); + deploy_run.addFileArg(package); + deploy_run.addArg(b.fmt("{s}:{s}", .{itchio_target, channel})); + deploy_run.has_side_effects = true; + return deploy_run; } const ProjectResult = union(enum) { native: *std.Build.Step.Compile, wasm: *std.Build.Step.InstallDir, + + pub fn createPackage(self: ProjectResult, ctx: Context, output_name: []const u8) Build.LazyPath { + switch (self) { + .native => |exe| { + return buildZip(ctx, output_name, &.{ + .{ .file = exe.getEmittedBin() } + }); + }, + .wasm => |install_dir| { + return buildZip(ctx, output_name, &.{ + .{ .directory = install_dir.options.source_dir } + }); + } + } + } }; -fn buildProject(ctx: Context, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !ProjectResult { +fn getPackageSuffix(target: Build.ResolvedTarget) []const u8 { + return switch(target.result.os.tag) { + .emscripten => "web", + .linux => "linux", + .windows => "windows", + else => @panic("Unsupported target") + }; +} + +const ProjectOptions = struct { + imgui: ?bool = null, + assets_from_filesystem: ?bool = null, + tracy: ?bool = null, +}; + +fn buildProject(ctx: Context, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, opts: ProjectOptions) !ProjectResult { const b = ctx.b; const assets_dir = b.path("assets"); @@ -80,15 +150,15 @@ fn buildProject(ctx: Context, target: Build.ResolvedTarget, optimize: std.builti const is_wasm = target.result.cpu.arch.isWasm(); const is_debug = optimize == .debug; - const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse is_debug; + const has_imgui = opts.imgui orelse is_debug; var assets_from_filesystem = false; if (!is_wasm) { - assets_from_filesystem = b.option(bool, "assets-filesystem", "Allow reading assets from filesystem") orelse is_debug; + assets_from_filesystem = opts.assets_from_filesystem orelse is_debug; } var has_tracy = false; if (!is_wasm) { - has_tracy = b.option(bool, "tracy", "Tracy integration") orelse is_debug; + has_tracy = opts.tracy orelse is_debug; } const win32_has_console = null; @@ -148,12 +218,15 @@ fn buildProject(ctx: Context, target: Build.ResolvedTarget, optimize: std.builti slang.metal_macos = true; slang.glsl300es = true; } - const mod_shader = try sokol.shdc.createModule(b, "shader", mod_sokol, .{ - .shdc_dep = dep_shdc, - .input = "src/platform/shader.glsl", - .output = "src/platform/shader.zig", - .slang = slang + const mod_shader = b.createModule(.{ + .root_source_file = try sokol.shdc.compile(b, .{ + .shdc_dep = dep_shdc, + .input = "src/platform/shader.glsl", + .output = "src/platform/shader.zig", + .slang = slang + }) }); + mod_shader.addImport("sokol", mod_sokol); mod_shader.addImport("math", math_mod); platform_mod.addImport("shader", mod_shader); @@ -185,12 +258,37 @@ fn buildProject(ctx: Context, target: Build.ResolvedTarget, optimize: std.builti } if (is_wasm) { + exe_mod.sanitize_c = .off; + + // build the main file into a library, this is because the WASM 'exe' + // needs to be linked in a separate build step with the Emscripten linker + const main_lib = b.addLibrary(.{ + .name = "index", + .root_module = exe_mod + }); + + const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{}); + + patchWasmIncludeDirs( + exe_mod, + dep_emsdk.path("upstream/emscripten/cache/sysroot/include"), + &(dep_sokol.artifact("sokol_clib").step) + ); + + const link_step = try sokol.emLinkStep(b, .{ + .lib_main = main_lib, + .target = target, + .optimize = optimize, + .emsdk = dep_emsdk, + .use_webgl2 = true, + .use_emmalloc = true, + .use_filesystem = true, + .shell_file_path = b.path("src/platform/shell.html"), + .extra_args = &.{ "-sALLOW_MEMORY_GROWTH", "-sENVIRONMENT=web" } + }); + return ProjectResult{ - .wasm = buildWasm(b, .{ - .name = "index", - .root_module = exe_mod, - .dep_sokol = dep_sokol, - }) + .wasm = link_step }; } else { const exe = b.addExecutable(.{ @@ -310,48 +408,22 @@ fn buildWin32ResourceFile(ctx: Context, mod: *Build.Module, opts: Win32ResourceF mod.addWin32ResourceFile(.{ .file = resource_file }); } -const BuildWasmOptions = struct { - name: []const u8, - root_module: *std.Build.Module, - dep_sokol: *std.Build.Dependency, +const ZipEntry = union(enum) { + directory: Build.LazyPath, + file: Build.LazyPath }; -fn buildWasm(b: *std.Build, opts: BuildWasmOptions) *std.Build.Step.InstallDir { - opts.root_module.sanitize_c = .off; +fn buildZip(ctx: Context, output_name: []const u8, entries: []const ZipEntry) Build.LazyPath { + const b = ctx.b; - // build the main file into a library, this is because the WASM 'exe' - // needs to be linked in a separate build step with the Emscripten linker - const main_lib = b.addLibrary(.{ - .name = opts.name, - .root_module = opts.root_module, - }); - - const dep_sokol = opts.dep_sokol; - const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{}); - - // TODO: - // const emsdk_install_step = sokol.emSdkInstallStep(b, dep_emsdk, .{}); - // main_lib.step.dependOn(emsdk_install_step); - - patchWasmIncludeDirs( - opts.root_module, - dep_emsdk.path("upstream/emscripten/cache/sysroot/include"), - &(dep_sokol.artifact("sokol_clib").step) - ); - - const link_step = sokol.emLinkStep(b, .{ - .lib_main = main_lib, - .target = opts.root_module.resolved_target.?, - .optimize = opts.root_module.optimize.?, - .emsdk = dep_emsdk, - .use_webgl2 = true, - .use_emmalloc = true, - .use_filesystem = true, - .shell_file_path = b.path("src/platform/shell.html"), - .extra_args = &.{ "-sALLOW_MEMORY_GROWTH", "-sENVIRONMENT=web" } - }) catch unreachable; - - return link_step; + var create_zip_run = b.addRunArtifact(ctx.create_zip); + for (entries) |entry| { + switch (entry) { + .directory => |lp| create_zip_run.addDirectoryArg(lp), + .file => |lp| create_zip_run.addFileArg(lp), + } + } + return create_zip_run.addOutputFileArg(output_name); } fn patchWasmIncludeDirs( diff --git a/build.zig.zon b/build.zig.zon index fe8ec58..2fb0014 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ - .name = .sokol_template_v2, + .name = .sokoban, .version = "0.0.0", - .fingerprint = 0xf440b4e6e51f5206, // Changing this has security and trust implications. + .fingerprint = 0xa95600b91ee22a2e, // Changing this has security and trust implications. .dependencies = .{ .sokol = .{ .url = "git+https://github.com/floooh/sokol-zig.git#e371234960812882406a296c4f3154321a16ee4d", diff --git a/scripts/create-zip.zig b/scripts/create-zip.zig index 2e8e087..b21329b 100644 --- a/scripts/create-zip.zig +++ b/scripts/create-zip.zig @@ -9,11 +9,11 @@ pub fn main(init: std.process.Init) !void { const args = try init.minimal.args.toSlice(arena); if (args.len != 3) { - std.debug.print("Usage: ./create-zip ", .{}); + std.debug.print("Usage: ./create-zip ", .{}); std.process.exit(1); } - const input_directory_path = args[1]; + const input_path = args[1]; const output_zip_path = args[2]; const cwd = Io.Dir.cwd(); @@ -21,22 +21,36 @@ pub fn main(init: std.process.Init) !void { try archive.initFile(output_zip_path); defer archive.end(); - var input_dir = try cwd.openDir(io, input_directory_path, .{ .iterate = true }); - defer input_dir.close(io); + const input_stat = try cwd.statFile(io, input_path, .{}); + if (input_stat.kind == .file) { + try addFileToArchive(&archive, io, cwd, input_path); - var input_dir_iter = input_dir.iterate(); - while (try input_dir_iter.next(io)) |entry| { - if (entry.kind == .file) { - var path_buff: [std.fs.max_path_bytes+1]u8 = undefined; - const path_len = try input_dir.realPathFile(io, entry.name, path_buff[0..(path_buff.len-1)]); - path_buff[path_len] = 0; + } else if (input_stat.kind == .directory) { + var input_dir = try cwd.openDir(io, input_path, .{ .iterate = true }); + defer input_dir.close(io); - var namez_buff: [std.fs.max_name_bytes+1]u8 = undefined; - @memcpy(namez_buff[0..entry.name.len], entry.name); - namez_buff[entry.name.len] = 0; - try archive.addFile(namez_buff[0..entry.name.len :0], path_buff[0..path_len :0]); + var input_dir_iter = input_dir.iterate(); + while (try input_dir_iter.next(io)) |entry| { + if (entry.kind == .file) { + try addFileToArchive(&archive, io, input_dir, entry.name); + } } + } else { + std.debug.print("Invalid input\n", .{}); + std.process.exit(1); } try archive.finalize(); } + +fn addFileToArchive(archive: *miniz.Archive, io: Io, dir: Io.Dir, sub_path: []const u8) !void { + var path_buff: [std.fs.max_path_bytes+1]u8 = undefined; + const path_len = try dir.realPathFile(io, sub_path, path_buff[0..(path_buff.len-1)]); + path_buff[path_len] = 0; + + const name = std.fs.path.basename(sub_path); + var namez_buff: [std.fs.max_name_bytes+1]u8 = undefined; + @memcpy(namez_buff[0..name.len], name); + namez_buff[name.len] = 0; + try archive.addFile(namez_buff[0..name.len :0], path_buff[0..path_len :0]); +}