const std = @import("std"); const bh = @import("bh"); pub fn addShaderDefinition( b: *std.Build, comptime sdl3Path: []const u8, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, shaderName: []const u8, jsonPath: std.Build.LazyPath, ) *std.Build.Module { // Create the spvReflect2 executable const spvReflect = b.addExecutable(.{ .name = "spvReflect2", .root_module = b.createModule(.{ .target = b.graph.host, .optimize = optimize, .root_source_file = b.path(sdl3Path ++ "/spvreflect/spvReflect2.zig"), }), }); // Run the spvReflect2 executable const runReflect = b.addRunArtifact(spvReflect); runReflect.addFileArg(jsonPath); const reflectedZigOut = runReflect.addOutputFileArg(b.fmt("reflected/{s}.zig", .{shaderName})); const module = b.createModule(.{ .root_source_file = reflectedZigOut, .optimize = optimize }); const dep = b.dependency("shaderTypes", .{ .target = target, .optimize = optimize, }); module.addImport("shaderTypes", dep.module("shaderTypes")); return module; } pub fn shaderDefintion( b: *std.Build, module: *std.Build.Module, comptime sdl3Path: []const u8, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, shaderName: []const u8, jsonPath: std.Build.LazyPath, ) void { const mod = addShaderDefinition(b, sdl3Path, target, optimize, shaderName, jsonPath); module.addImport(shaderName, mod); } pub fn build(b: *std.Build) void { const opts = bh.declareOptions(b); const preferred_linkage: std.builtin.LinkMode = if (opts.static_build) .static else .dynamic; const sdl_dep = b.dependency("sdl", .{ .target = opts.target, .optimize = opts.optimize, .preferred_linkage = preferred_linkage, }); const sdl3_lib = sdl_dep.artifact("SDL3"); const sdl3_fwd = b.addModule("SDL3", .{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("src/sdl3_lib_fwd.zig"), }); sdl3_fwd.linkLibrary(sdl3_lib); const mod = b.addModule("sdl3", .{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("src/sdl3.zig"), }); const shaderTypes = b.dependency("shaderTypes", .{ .target = opts.target, .optimize = opts.optimize, }); mod.addImport("shaderTypes", shaderTypes.module("shaderTypes")); mod.addIncludePath(b.path("SDL/include")); mod.linkLibrary(sdl3_lib); const test_step2 = b.step("test", "run unit tests for sdl3"); const tests2 = b.addExecutable(.{ .name = "hello-sdl", .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("src/samples/compiletest.zig"), .link_libc = true, }), }); const test_step = b.step("test-triangle", "run unit tests for sdl3"); const tests = b.addExecutable(.{ .name = "hello-triangle", .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("src/samples/hello-triangle.zig"), .link_libc = true, }), }); const hello_window = b.step("hello-window", "test window"); const hello_window_exe = b.addExecutable(.{ .name = "hello-window", .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("src/samples/hello-window.zig"), .link_libc = true, }), }); hello_window.dependOn(&hello_window_exe.step); hello_window_exe.root_module.addImport("sdl3", mod); tests.root_module.addImport("sdl3", mod); const vertexDefinitions = addShaderDefinition(b, ".", opts.target, opts.optimize, "hello-triangle.vert", b.path("content/hello-triangle.vert.json")); tests.root_module.addImport("hello-triangle.vert", vertexDefinitions); const runArtifact = b.addRunArtifact(tests); test_step.dependOn(&runArtifact.step); const runArtifact2 = b.addRunArtifact(tests2); test_step2.dependOn(&runArtifact2.step); b.installArtifact(hello_window_exe); b.installArtifact(sdl3_lib); b.installArtifact(tests); b.installArtifact(tests2); // Regenerate bindings for multiple SDL headers const parser_dep = b.dependency("sdl3_parser", .{ .target = opts.target, .optimize = opts.optimize, }); const parser_exe = parser_dep.artifact("sdl-parser"); // All public SDL3 API headers (53 total) // Skipped: assert, thread, hidapi, mutex, tray (not core APIs or problematic) const headers_to_generate = [_]struct { header: []const u8, output: []const u8 }{ // .{ .header = "SDL/include/SDL3/SDL_asyncio.h", .output = "asyncio" }, // .{ .header = "SDL/include/SDL3/SDL_atomic.h", .output = "atomic" }, .{ .header = "SDL/include/SDL3/SDL_audio.h", .output = "audio" }, .{ .header = "SDL/include/SDL3/SDL_blendmode.h", .output = "blendmode" }, .{ .header = "SDL/include/SDL3/SDL_camera.h", .output = "camera" }, .{ .header = "SDL/include/SDL3/SDL_clipboard.h", .output = "clipboard" }, // .{ .header = "SDL/include/SDL3/SDL_cpuinfo.h", .output = "cpuinfo" }, .{ .header = "SDL/include/SDL3/SDL_dialog.h", .output = "dialog" }, .{ .header = "SDL/include/SDL3/SDL_endian.h", .output = "endian" }, .{ .header = "SDL/include/SDL3/SDL_error.h", .output = "error" }, // .{ .header = "SDL/include/SDL3/SDL_events.h", .output = "events" }, .{ .header = "SDL/include/SDL3/SDL_filesystem.h", .output = "filesystem" }, .{ .header = "SDL/include/SDL3/SDL_gamepad.h", .output = "gamepad" }, .{ .header = "SDL/include/SDL3/SDL_gpu.h", .output = "gpu" }, // .{ .header = "SDL/include/SDL3/SDL_guid.h", .output = "guid" }, .{ .header = "SDL/include/SDL3/SDL_haptic.h", .output = "haptic" }, // .{ .header = "SDL/include/SDL3/SDL_hidapi.h", .output = "hidapi" }, // Skipped: not core API .{ .header = "SDL/include/SDL3/SDL_hints.h", .output = "hints" }, .{ .header = "SDL/include/SDL3/SDL_init.h", .output = "init" }, // .{ .header = "SDL/include/SDL3/SDL_iostream.h", .output = "iostream" }, // Skipped: complex I/O API .{ .header = "SDL/include/SDL3/SDL_joystick.h", .output = "joystick" }, // .{ .header = "SDL/include/SDL3/SDL_keyboard.h", .output = "keyboard" }, .{ .header = "SDL/include/SDL3/SDL_keycode.h", .output = "keycode" }, .{ .header = "SDL/include/SDL3/SDL_loadso.h", .output = "loadso" }, // .{ .header = "SDL/include/SDL3/SDL_locale.h", .output = "locale" }, // .{ .header = "SDL/include/SDL3/SDL_log.h", .output = "log" }, .{ .header = "SDL/include/SDL3/SDL_messagebox.h", .output = "messagebox" }, // .{ .header = "SDL/include/SDL3/SDL_metal.h", .output = "metal" }, .{ .header = "SDL/include/SDL3/SDL_misc.h", .output = "misc" }, .{ .header = "SDL/include/SDL3/SDL_mouse.h", .output = "mouse" }, // .{ .header = "SDL/include/SDL3/SDL_mutex.h", .output = "mutex" }, // Skipped: not core API // .{ .header = "SDL/include/SDL3/SDL_opengl.h", .output = "opengl" }, // .{ .header = "SDL/include/SDL3/SDL_pen.h", .output = "pen" }, .{ .header = "SDL/include/SDL3/SDL_pixels.h", .output = "pixels" }, // .{ .header = "SDL/include/SDL3/SDL_power.h", .output = "power" }, // .{ .header = "SDL/include/SDL3/SDL_process.h", .output = "process" }, .{ .header = "SDL/include/SDL3/SDL_properties.h", .output = "properties" }, .{ .header = "SDL/include/SDL3/SDL_rect.h", .output = "rect" }, .{ .header = "SDL/include/SDL3/SDL_render.h", .output = "render" }, // .{ .header = "SDL/include/SDL3/SDL_scancode.h", .output = "scancode" }, .{ .header = "SDL/include/SDL3/SDL_sensor.h", .output = "sensor" }, .{ .header = "SDL/include/SDL3/SDL_storage.h", .output = "storage" }, .{ .header = "SDL/include/SDL3/SDL_surface.h", .output = "surface" }, .{ .header = "SDL/include/SDL3/SDL_system.h", .output = "system" }, // .{ .header = "SDL/include/SDL3/SDL_thread.h", .output = "thread" }, // Skipped: not core API .{ .header = "SDL/include/SDL3/SDL_time.h", .output = "time" }, .{ .header = "SDL/include/SDL3/SDL_timer.h", .output = "timer" }, .{ .header = "SDL/include/SDL3/SDL_touch.h", .output = "touch" }, // .{ .header = "SDL/include/SDL3/SDL_tray.h", .output = "tray" }, // Skipped: not core API .{ .header = "SDL/include/SDL3/SDL_version.h", .output = "version" }, .{ .header = "SDL/include/SDL3/SDL_video.h", .output = "video" }, // .{ .header = "SDL/include/SDL3/SDL_vulkan.h", .output = "vulkan" }, // Skipped: Vulkan interop }; const regenerate_step = b.step("regenerate-zig", "Regenerate bindings from SDL headers"); for (headers_to_generate) |header_info| { const regenerate = b.addRunArtifact(parser_exe); regenerate.addFileArg(b.path(header_info.header)); regenerate.addArg(b.fmt("--output=v2/{s}.zig", .{header_info.output})); regenerate_step.dependOn(®enerate.step); const regenerateJson = b.addRunArtifact(parser_exe); regenerateJson.addFileArg(b.path(header_info.header)); regenerateJson.addArg(b.fmt("--generate-json=json/{s}.json", .{header_info.output})); regenerate_step.dependOn(®enerateJson.step); } // Regenerate test mocks step - using SDL_gpu.h for comprehensive testing const test_header_path = b.path("SDL/include/SDL3/SDL_gpu.h"); const test_zig_output = b.path("zig-out/gpu_test.zig"); const test_mock_output = b.path("zig-out/gpu_test_mock.c"); const regenerate_test_mocks = b.addRunArtifact(parser_exe); regenerate_test_mocks.addFileArg(test_header_path); regenerate_test_mocks.addArg(b.fmt("--output={s}", .{test_zig_output.getPath(b)})); regenerate_test_mocks.addArg(b.fmt("--mocks={s}", .{test_mock_output.getPath(b)})); const regenerate_test_mocks_step = b.step("regenerate-test-mocks", "Regenerate test bindings and mocks"); regenerate_test_mocks_step.dependOn(®enerate_test_mocks.step); // Compile mocks into a static library const mock_lib = b.addLibrary(.{ .name = "test_mocks", .linkage = .static, .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, }), }); mock_lib.addCSourceFile(.{ .file = test_mock_output, .flags = &.{"-std=c99"}, }); mock_lib.addIncludePath(b.path("SDL/include")); mock_lib.linkLibC(); mock_lib.step.dependOn(®enerate_test_mocks.step); // Compile-only check for generated bindings (no tests, just verify it compiles) const compile_check = b.addTest(.{ .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("parser/test/mock_test.zig"), }), }); compile_check.linkLibrary(mock_lib); compile_check.step.dependOn(&mock_lib.step); const compile_check_step = b.step("check-mocks", "Compile check for generated mocks (no tests)"); compile_check_step.dependOn(&compile_check.step); // Test executable that uses the generated bindings and mocks const mock_test = b.addTest(.{ .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("parser/test/mock_test.zig"), }), }); mock_test.linkLibrary(mock_lib); mock_test.step.dependOn(&mock_lib.step); const run_mock_test = b.addRunArtifact(mock_test); run_mock_test.step.dependOn(&mock_test.step); const test_mock_step = b.step("test-mocks", "Compile and test generated mocks"); test_mock_step.dependOn(&run_mock_test.step); // Additional test that demonstrates the dependency issue const import_test = b.addTest(.{ .root_module = b.createModule(.{ .target = opts.target, .optimize = opts.optimize, .root_source_file = b.path("parser/test/import_test.zig"), }), }); const run_import_test = b.addRunArtifact(import_test); const import_test_step = b.step("test-import-issue", "Test demonstrating missing dependency types"); import_test_step.dependOn(&run_import_test.step); }