const std = @import("std"); const mem = std.mem; const seizer_solitaire_version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 }; // Although this function looks imperative, note that its job is to // declaratively construct a build graph that will be executed by an external // runner. pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const version = try getVersionString(b); const exe = try addSeizerSolitaireExe(b, b.getInstallStep(), .{ .target = target, .optimize = optimize, .version = version }); const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); if (b.args) |args| { run_cmd.addArgs(args); } const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); // build for all targets const dist_step = b.step("dist", "Build binaries for all targets"); _ = try addSeizerSolitaireExe(b, dist_step, .{ .optimize = optimize, .version = version, .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .gnu }) }); _ = try addSeizerSolitaireExe(b, dist_step, .{ .optimize = optimize, .version = version, .target = b.resolveTargetQuery(.{ .cpu_arch = .arm, .os_tag = .linux, .abi = .gnueabihf }) }); _ = try addSeizerSolitaireExe(b, dist_step, .{ .optimize = optimize, .version = version, .target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }) }); _ = try addSeizerSolitaireExe(b, dist_step, .{ .optimize = optimize, .version = version, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi, .abi = .none }) }); } pub fn addSeizerSolitaireExe(b: *std.Build, install_step: *std.Build.Step, options: struct { target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, version: [:0]const u8, }) !*std.Build.Step.Compile { const seizer = b.dependency("seizer", .{ .target = options.target, .optimize = options.optimize, }); const exe = b.addExecutable(.{ .name = "seizer-solitaire", .root_source_file = b.path("src/main.zig"), .target = options.target, .optimize = options.optimize, }); exe.root_module.addImport("seizer", seizer.module("seizer")); const install_exe = b.addInstallArtifact(exe, .{ .dest_sub_path = b.fmt("{s}/{s}", .{ try options.target.result.linuxTriple(b.allocator), exe.name }) }); install_step.dependOn(&install_exe.step); // additionally generate an HTML file with the wasm module embedded when we use the wasi target if (options.target.result.os.tag == .wasi) { exe.wasi_exec_model = .reactor; const bundle_webpage_exe = seizer.artifact("bundle-webpage"); const bundle_webpage = b.addRunArtifact(bundle_webpage_exe); bundle_webpage.addArtifactArg(exe); const install_html = b.addInstallFile(bundle_webpage.captureStdOut(), "www/seizer-solitaire.html"); install_step.dependOn(&install_html.step); } // code to add version to binary, stolen from the zig language's build.zig const exe_options = b.addOptions(); exe.root_module.addOptions("build_options", exe_options); exe_options.addOption([:0]const u8, "version", options.version); return exe; } pub fn getVersionString(b: *std.Build) ![:0]const u8 { const opt_version_string = b.option([]const u8, "version-string", "Override version string. Default is to find out with git."); if (opt_version_string) |version| { return try b.allocator.dupeZ(u8, version); } if (!std.process.can_spawn) { std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{}); std.process.exit(1); } const version_string = b.fmt("{d}.{d}.{d}", .{ seizer_solitaire_version.major, seizer_solitaire_version.minor, seizer_solitaire_version.patch }); var code: u8 = undefined; const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "describe", "--match", "*.*.*", "--tags", "--abbrev=9", }, &code, .Ignore) catch { return try b.allocator.dupeZ(u8, version_string); }; const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r"); switch (mem.count(u8, git_describe, "-")) { 0 => { // Tagged release version (e.g. 0.10.0). if (!mem.eql(u8, git_describe, version_string)) { std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe }); std.process.exit(1); } return try b.allocator.dupeZ(u8, version_string); }, 2 => { // Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9). var it = mem.splitScalar(u8, git_describe, '-'); const tagged_ancestor = it.first(); const commit_height = it.next().?; const commit_id = it.next().?; const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor); if (seizer_solitaire_version.order(ancestor_ver) != .gt) { std.debug.print("version '{}' must be greater than tagged ancestor '{}'\n", .{ seizer_solitaire_version, ancestor_ver }); std.process.exit(1); } // Check that the commit hash is prefixed with a 'g' (a Git convention). if (commit_id.len < 1 or commit_id[0] != 'g') { std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe}); return try b.allocator.dupeZ(u8, version_string); } // The version is reformatted in accordance with the https://semver.org specification. const formatted_version = b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] }); return try b.allocator.dupeZ(u8, formatted_version); }, else => { std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe}); return try b.allocator.dupeZ(u8, version_string); }, } }