const std = @import("std"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const is_wasm = target.result.cpu.arch.isWasm(); const is_debug = (optimize == .Debug); const has_tracy = b.option(bool, "tracy", "Tracy integration") orelse is_debug; const has_imgui = b.option(bool, "imgui", "ImGui integration") orelse is_debug; const exe_mod = b.createModule(.{ .root_source_file = b.path("src/main.zig"), }); const build_options = b.addOptions(); build_options.addOption(bool, "has_imgui", has_imgui); exe_mod.addOptions("build_options", build_options); const dep_sokol = b.dependency("sokol", .{ .target = target, .optimize = optimize, .with_sokol_imgui = has_imgui }); exe_mod.linkLibrary(dep_sokol.artifact("sokol_clib")); exe_mod.addImport("sokol", dep_sokol.module("sokol")); if (has_imgui) { if (b.lazyDependency("cimgui", .{ .target = target, .optimize = optimize, })) |dep_cimgui| { const cimgui = b.lazyImport(@This(), "cimgui").?; const cimgui_conf = cimgui.getConfig(true); exe_mod.addImport("cimgui", dep_cimgui.module(cimgui_conf.module_name)); dep_sokol.artifact("sokol_clib").root_module.addIncludePath(dep_cimgui.path(cimgui_conf.include_dir)); } } const dep_stb = b.dependency("stb", .{}); exe_mod.addImport("stb_image", dep_stb.module("stb_image")); exe_mod.addImport("stb_vorbis", dep_stb.module("stb_vorbis")); const dep_tracy = b.dependency("tracy", .{ .target = target, .optimize = optimize, .tracy_enable = has_tracy, .tracy_only_localhost = true }); if (has_tracy) { exe_mod.linkLibrary(dep_tracy.artifact("tracy")); } exe_mod.addImport("tracy", dep_tracy.module("tracy")); if (is_wasm) { // TODO: } else { exe_mod.resolved_target = target; exe_mod.optimize = optimize; const exe = buildNative(b, .{ .name = "sokol_template_v2", .root_module = exe_mod }); b.installArtifact(exe); { 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); } { const exe_tests = b.addTest(.{ .root_module = exe_mod, }); const run_exe_tests = b.addRunArtifact(exe_tests); const test_step = b.step("test", "Run tests"); test_step.dependOn(&run_exe_tests.step); } } } const BuildNativeOptions = struct { name: []const u8, root_module: *std.Build.Module, win32_has_console: ?bool = null, win32_png_icon: ?std.Build.LazyPath = null }; fn buildNative( b: *std.Build, opts: BuildNativeOptions, ) *std.Build.Step.Compile { const exe = b.addExecutable(.{ .name = opts.name, .root_module = opts.root_module, }); const target = opts.root_module.resolved_target.?; if (target.result.os.tag == .windows) { const optimize = opts.root_module.optimize.?; const has_console = opts.win32_has_console orelse (optimize == .Debug); exe.subsystem = if (has_console) .Console else .Windows; if (opts.win32_png_icon) |win32_png_icon| { const png_to_icon_tool = buildPngToIconTool(b, b.graph.host, .ReleaseSafe); const png_to_icon_step = b.addRunArtifact(png_to_icon_tool); png_to_icon_step.addFileArg(win32_png_icon); const icon_file = png_to_icon_step.addOutputFileArg("icon.ico"); const add_icon_step = AddExecutableIcon.init(exe, icon_file); exe.step.dependOn(&add_icon_step.step); } } return exe; } fn buildPngToIconTool( b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, ) *std.Build.Step.Compile { const mod = b.createModule(.{ .target = target, .optimize = optimize, .root_source_file = b.path("tools/png-to-icon.zig"), }); const dep_stb = b.dependency("stb", .{}); mod.addImport("stb_image", dep_stb.module("stb_image")); return b.addExecutable(.{ .name = "png-to-icon", .root_module = mod, }); } const AddExecutableIcon = struct { obj: *std.Build.Step.Compile, step: std.Build.Step, icon_file: std.Build.LazyPath, resource_file: std.Build.LazyPath, fn init(obj: *std.Build.Step.Compile, icon_file: std.Build.LazyPath) *AddExecutableIcon { const b = obj.step.owner; const self = b.allocator.create(AddExecutableIcon) catch @panic("OOM"); self.obj = obj; self.step = std.Build.Step.init(.{ .id = .custom, .name = "add executable icon", .owner = b, .makeFn = make }); self.icon_file = icon_file; icon_file.addStepDependencies(&self.step); const write_files = b.addWriteFiles(); self.resource_file = write_files.add("resource-file.rc", ""); self.step.dependOn(&write_files.step); self.obj.root_module.addWin32ResourceFile(.{ .file = self.resource_file, }); return self; } fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void { const b = step.owner; const self: *AddExecutableIcon = @fieldParentPtr("step", step); const relative_icon_path = try std.fs.path.relative( b.allocator, "", null, self.resource_file.dirname().getPath(b), self.icon_file.getPath(b) ); std.mem.replaceScalar(u8, relative_icon_path, '\\', '/'); { const cwd = std.Io.Dir.cwd(); const resource_file = try cwd.createFile(b.graph.io, self.resource_file.getPath(b), .{ }); defer resource_file.close(b.graph.io); var buff: [4*4096]u8 = undefined; var file_writer = resource_file.writer(b.graph.io, &buff); const w = &file_writer.interface; try w.writeAll("IDI_ICON ICON \""); try w.writeAll(relative_icon_path); try w.writeAll("\""); } } };