sdlparser-scrap/build.zig

243 lines
10 KiB
Zig

const std = @import("std");
fn addFetchSdlStep(b: *std.Build) *std.Build.Step {
const default_sdl_url = "git@github.com:castholm/SDL.git";
const official_sdl_url = "git@github.com:libsdl-org/SDL.git";
const sdl_url_option = b.option([]const u8, "sdl-url", "URL to fetch SDL3 from (use 'official' for libsdl-org/SDL)") orelse default_sdl_url;
const sdl_url = if (std.mem.eql(u8, sdl_url_option, "official") or
b.option(bool, "official", "use the official sdl path of git@github.com:libsdl-org/SDL.git (same as setting -Dsdl-url=official)") != null)
official_sdl_url
else
sdl_url_option;
const clean_sdl = b.option(bool, "clean", "Delete sdl3/ directory before fetching") orelse false;
const sdl_checkout = b.option([]const u8, "ref", "the git ref to check out after fetching sdl, this can be tag, commit, branch...");
const fetch_step = b.step("fetch-sdl", "Fetch SDL3 source from git");
const sdl_exists = if (std.fs.cwd().access("sdl3", .{})) true else |_| false;
var end_step: *std.Build.Step = undefined;
if (clean_sdl) {
const remove_dir = b.addRemoveDirTree(b.path("sdl3"));
const fetch_sdl = b.addSystemCommand(&.{ "git", "clone", sdl_url, "sdl3" });
fetch_sdl.step.dependOn(&remove_dir.step);
end_step = &fetch_sdl.step;
// fetch_step.dependOn(&fetch_sdl.step);
} else if (sdl_exists) {
const echo_msg = b.addSystemCommand(&.{ "echo", "sdl already fetched" });
end_step = &echo_msg.step;
// fetch_step.dependOn(&echo_msg.step);
} else {
const fetch_sdl = b.addSystemCommand(&.{ "git", "clone", sdl_url, "sdl3" });
end_step = &fetch_sdl.step;
}
const checkout_step = if (sdl_checkout) |ref| &b.addSystemCommand(&.{ "git", "-C", "sdl3", "checkout", ref }).step else end_step;
fetch_step.dependOn(checkout_step);
return fetch_step;
}
const ArchiveStep = struct {
step: std.Build.Step,
pub fn create(b: *std.Build) *std.Build.Step {
const self = b.allocator.create(ArchiveStep) catch @panic("OOM");
self.* = .{
.step = std.Build.Step.init(.{
.id = .custom,
.name = "archive generate outputs",
.owner = b,
.makeFn = make,
}),
};
return &self.step;
}
fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void {
_ = step;
_ = options;
const cwd = std.fs.cwd();
const json_exists = if (cwd.access("json", .{})) true else |_| false;
const v2_exists = if (cwd.access("v2", .{})) true else |_| false;
if (!json_exists and !v2_exists) return;
const timestamp = std.time.timestamp();
var buf: [64]u8 = undefined;
const archive_path = try std.fmt.bufPrint(&buf, "archive/generate/{d}", .{timestamp});
try cwd.makePath(archive_path);
if (json_exists) {
var json_dest_buf: [128]u8 = undefined;
const json_dest = try std.fmt.bufPrint(&json_dest_buf, "{s}/json", .{archive_path});
try cwd.rename("json", json_dest);
}
if (v2_exists) {
var v2_dest_buf: [128]u8 = undefined;
const v2_dest = try std.fmt.bufPrint(&v2_dest_buf, "{s}/v2", .{archive_path});
try cwd.rename("v2", v2_dest);
}
}
};
pub fn generateApi(b: *std.Build, parser_exe: *std.Build.Step.Compile, fetch_sdl_step: *std.Build.Step) void {
// Archive existing json/ and v2/ directories before regenerating
const archive_step = ArchiveStep.create(b);
fetch_sdl_step.dependOn(archive_step);
// Write a build marker file after fetch to enable caching
const timestamp = std.time.timestamp();
const wf = b.addWriteFiles();
const marker_file = wf.add(".buildmarker", b.fmt("{d}", .{timestamp}));
_ = marker_file;
wf.step.dependOn(fetch_sdl_step);
// 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_asyncio.h", .output = "asyncio" },
// .{ .header = "SDL_atomic.h", .output = "atomic" },
.{ .header = "SDL_audio.h", .output = "audio" },
.{ .header = "SDL_blendmode.h", .output = "blendmode" },
.{ .header = "SDL_camera.h", .output = "camera" },
.{ .header = "SDL_clipboard.h", .output = "clipboard" },
// .{ .header = "SDL_cpuinfo.h", .output = "cpuinfo" },
.{ .header = "SDL_dialog.h", .output = "dialog" },
.{ .header = "SDL_endian.h", .output = "endian" },
.{ .header = "SDL_error.h", .output = "error" },
// .{ .header = "SDL_events.h", .output = "events" },
.{ .header = "SDL_filesystem.h", .output = "filesystem" },
.{ .header = "SDL_gamepad.h", .output = "gamepad" },
.{ .header = "SDL_gpu.h", .output = "gpu" },
// .{ .header = "SDL_guid.h", .output = "guid" },
.{ .header = "SDL_haptic.h", .output = "haptic" },
// .{ .header = "SDL_hidapi.h", .output = "hidapi" }, // Skipped: not core API
.{ .header = "SDL_hints.h", .output = "hints" },
.{ .header = "SDL_init.h", .output = "init" },
// .{ .header = "SDL_iostream.h", .output = "iostream" }, // Skipped: complex I/O API
.{ .header = "SDL_joystick.h", .output = "joystick" },
// .{ .header = "SDL_keyboard.h", .output = "keyboard" },
.{ .header = "SDL_keycode.h", .output = "keycode" },
.{ .header = "SDL_loadso.h", .output = "loadso" },
// .{ .header = "SDL_locale.h", .output = "locale" },
// .{ .header = "SDL_log.h", .output = "log" },
.{ .header = "SDL_messagebox.h", .output = "messagebox" },
// .{ .header = "SDL_metal.h", .output = "metal" },
.{ .header = "SDL_misc.h", .output = "misc" },
.{ .header = "SDL_mouse.h", .output = "mouse" },
// .{ .header = "SDL_mutex.h", .output = "mutex" }, // Skipped: not core API
// .{ .header = "SDL_opengl.h", .output = "opengl" },
// .{ .header = "SDL_pen.h", .output = "pen" },
.{ .header = "SDL_pixels.h", .output = "pixels" },
// .{ .header = "SDL_power.h", .output = "power" },
// .{ .header = "SDL_process.h", .output = "process" },
.{ .header = "SDL_properties.h", .output = "properties" },
.{ .header = "SDL_rect.h", .output = "rect" },
.{ .header = "SDL_render.h", .output = "render" },
// .{ .header = "SDL_scancode.h", .output = "scancode" },
.{ .header = "SDL_sensor.h", .output = "sensor" },
.{ .header = "SDL_storage.h", .output = "storage" },
.{ .header = "SDL_surface.h", .output = "surface" },
.{ .header = "SDL_system.h", .output = "system" },
// .{ .header = "SDL_thread.h", .output = "thread" }, // Skipped: not core API
.{ .header = "SDL_time.h", .output = "time" },
.{ .header = "SDL_timer.h", .output = "timer" },
.{ .header = "SDL_touch.h", .output = "touch" },
// .{ .header = "SDL_tray.h", .output = "tray" }, // Skipped: not core API
.{ .header = "SDL_version.h", .output = "version" },
.{ .header = "SDL_video.h", .output = "video" },
// .{ .header = "SDL_vulkan.h", .output = "vulkan" }, // Skipped: Vulkan interop
};
const regenerate_step = b.step("generate", "Regenerate bindings from SDL headers");
const header_path = "sdl3/include/SDL3";
const timestamp_arg = b.fmt("--timestamp={d}", .{timestamp});
for (headers_to_generate) |header_info| {
const regenerate = b.addRunArtifact(parser_exe);
regenerate.addFileArg(b.path(b.fmt("{s}/{s}", .{ header_path, header_info.header })));
regenerate.addArg(b.fmt("--output=v2/{s}.zig", .{header_info.output}));
regenerate.addArg(timestamp_arg);
// regenerate.addArg(b.fmt("--output=v2/{s}.zig --mocks=mocks/{s}.c", .{ header_info.output, header_info.output }));
regenerate.step.dependOn(&wf.step);
regenerate_step.dependOn(&regenerate.step);
const regenerateJson = b.addRunArtifact(parser_exe);
regenerateJson.addFileArg(b.path(b.fmt("{s}/{s}", .{ header_path, header_info.header })));
regenerateJson.addArg(b.fmt("--generate-json=json/{s}.json", .{header_info.output}));
regenerateJson.addArg(timestamp_arg);
regenerateJson.step.dependOn(&wf.step);
regenerate_step.dependOn(&regenerateJson.step);
}
}
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const fetch_sdl_step = addFetchSdlStep(b);
// Parser executable
const parser_exe = b.addExecutable(.{
.name = "sdl-parser",
.root_module = b.createModule(.{
.root_source_file = b.path("src/parser.zig"),
.target = target,
.optimize = optimize,
}),
});
b.installArtifact(parser_exe);
generateApi(b, parser_exe, fetch_sdl_step);
// Run command
const run_cmd = b.addRunArtifact(parser_exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the SDL3 header parser");
run_step.dependOn(&run_cmd.step);
// Test mocks generation target
const test_mocks_cmd = b.addRunArtifact(parser_exe);
test_mocks_cmd.step.dependOn(b.getInstallStep());
const test_header_path = b.path("test_small.h");
const test_output = b.path("zig-out/test_small.zig");
const test_mocks = b.path("zig-out/test_small_mock.c");
test_mocks_cmd.addArg(test_header_path.getPath(b));
test_mocks_cmd.addArg(b.fmt("--output={s}", .{test_output.getPath(b)}));
test_mocks_cmd.addArg(b.fmt("--mocks={s}", .{test_mocks.getPath(b)}));
const test_mocks_step = b.step("test-mocks", "Test mock generation with test_small.h");
test_mocks_step.dependOn(&test_mocks_cmd.step);
// Tests
const parser_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/parser.zig"),
.target = target,
.optimize = optimize,
}),
});
const run_tests = b.addRunArtifact(parser_tests);
const test_step = b.step("test", "Run parser tests");
test_step.dependOn(&run_tests.step);
}