const builtin = @import("builtin"); const std = @import("std"); const SubPath = struct { buffer: [max]u8 = [_]u8{0} ** max, unused: u8 = max, const max = 255; pub fn utf8(self: *const SubPath) [:0]const u8 { return self.buffer[0 .. (max - self.unused):0]; } }; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const sokol_dependency = b.dependency("sokol", .{ .target = target, .optimize = optimize, }); const coral_module = b.createModule(.{ .root_source_file = b.path("src/coral/coral.zig"), }); const flow_module = b.createModule(.{ .root_source_file = b.path("src/flow/flow.zig"), .imports = &.{ .{ .name = "coral", .module = coral_module, }, } }); const ona_module = b.createModule(.{ .root_source_file = b.path("src/ona/ona.zig"), .imports = &.{ .{ .name = "sokol", .module = sokol_dependency.module("sokol"), }, .{ .name = "coral", .module = coral_module, }, .{ .name = "flow", .module = flow_module, }, }, }); ona_module.addIncludePath(b.path("ext/")); ona_module.linkLibrary(spirv_cross: { const dir = "ext/spirv-cross/"; const sources = [_][]const u8{ "spirv_cross.cpp", "spirv_parser.cpp", "spirv_cross_parsed_ir.cpp", "spirv_cfg.cpp", "spirv_glsl.cpp", "spirv_msl.cpp", "spirv_hlsl.cpp", "spirv_reflect.cpp", "spirv_cross_util.cpp", "spirv_cross_c.cpp", }; const lib = b.addStaticLibrary(.{ .name = "spirvcross", .target = target, .optimize = optimize, }); switch (lib.rootModuleTarget().abi) { .msvc => lib.linkLibC(), else => lib.linkLibCpp(), } inline for (sources) |src| { lib.addCSourceFile(.{ .file = b.path(dir ++ src), .flags = &.{"-fstrict-aliasing", "-DSPIRV_CROSS_C_API_GLSL", "-DSPIRV_CROSS_C_API_HLSL", "-DSPIRV_CROSS_C_API_MSL"}, }); } break: spirv_cross lib; }); b.step("test", "Run unit tests").dependOn(tests: { const tests = b.addTest(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); break: tests &tests.step; }); b.installArtifact(add: { const exe = b.addExecutable(.{ .name = "main", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); exe.root_module.addImport("ona", ona_module); exe.root_module.addImport("coral", coral_module); exe.linkLibC(); exe.linkSystemLibrary("SDL2"); const shaders_sub_path = "src/ona/gfx/shaders/"; var shaders_dir = try std.fs.cwd().openDir(shaders_sub_path, .{ .iterate = true, }); defer shaders_dir.close(); var shaders_walker = try shaders_dir.walk(b.allocator); defer shaders_walker.deinit(); const Shader = struct { source_sub_path: SubPath = .{}, binary_sub_path: SubPath = .{}, }; var pending_shaders = std.ArrayList(Shader).init(b.allocator); defer pending_shaders.deinit(); scan_shaders: while (try shaders_walker.next()) |entry| { if (entry.kind != .file) { continue: scan_shaders; } const is_shader_file = std.mem.endsWith(u8, entry.path, ".frag") or std.mem.endsWith(u8, entry.path, ".vert"); if (!is_shader_file) { continue: scan_shaders; } const shader_name = std.fs.path.stem(entry.path); for (pending_shaders.items) |pending_shader| { if (std.mem.endsWith(u8, pending_shader.source_sub_path.utf8(), shader_name)) { continue: scan_shaders; } } var shader = Shader{}; const source_sub_path = try std.fmt.bufPrint(&shader.source_sub_path.buffer, "{s}{s}", .{shaders_sub_path, shader_name}); const binary_sub_path = try std.fmt.bufPrint(&shader.binary_sub_path.buffer, "{s}.spv", .{source_sub_path}); shaders_dir.access(std.fs.path.basename(binary_sub_path), .{.mode = .read_only}) catch { shader.source_sub_path.unused -= @intCast(source_sub_path.len); shader.binary_sub_path.unused -= @intCast(binary_sub_path.len); try pending_shaders.append(shader); continue: scan_shaders; }; if ((try shaders_dir.statFile(entry.basename)).mtime > (try shaders_dir.statFile(std.fs.path.basename(binary_sub_path))).mtime) { shader.source_sub_path.unused -= @intCast(source_sub_path.len); shader.binary_sub_path.unused -= @intCast(binary_sub_path.len); try pending_shaders.append(shader); continue: scan_shaders; } } for (pending_shaders.items) |pending_shader| { var vertex_binary_sub_path = SubPath{}; var fragment_binary_sub_path = SubPath{}; const source_sub_path_utf8 = pending_shader.source_sub_path.utf8(); vertex_binary_sub_path.unused -= @intCast((try std.fmt.bufPrint(&vertex_binary_sub_path.buffer, "{s}.vert.spv", .{source_sub_path_utf8})).len); fragment_binary_sub_path.unused -= @intCast((try std.fmt.bufPrint(&fragment_binary_sub_path.buffer, "{s}.frag.spv", .{source_sub_path_utf8})).len); const vertex_binary_sub_path_utf8 = vertex_binary_sub_path.utf8(); const fragment_binary_sub_path_utf8 = fragment_binary_sub_path.utf8(); const link_command = b.addSystemCommand(&.{ "spirv-link", vertex_binary_sub_path_utf8, fragment_binary_sub_path_utf8, "-o", pending_shader.binary_sub_path.utf8(), }); exe.step.dependOn(&link_command.step); link_command.step.dependOn(compile_vertex: { const compile_command = b.addSystemCommand(&.{ "glslangValidator", "-V", vertex_binary_sub_path_utf8[0 .. vertex_binary_sub_path_utf8.len - 4], "-o", vertex_binary_sub_path_utf8, }); break: compile_vertex &compile_command.step; }); link_command.step.dependOn(compile_fragment: { const compile_command = b.addSystemCommand(&.{ "glslangValidator", "-V", fragment_binary_sub_path_utf8[0 .. fragment_binary_sub_path_utf8.len - 4], "-o", fragment_binary_sub_path_utf8, }); break: compile_fragment &compile_command.step; }); } break: add exe; }); }