285 lines
12 KiB
Zig
285 lines
12 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;
|
|
|
|
if (sdl_checkout != null) {
|
|
checkout_step.dependOn(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 api_exists = if (cwd.access("api", .{})) true else |_| false;
|
|
|
|
if (!json_exists and !api_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 (api_exists) {
|
|
var api_dest_buf: [128]u8 = undefined;
|
|
const api_dest = try std.fmt.bufPrint(&api_dest_buf, "{s}/api", .{archive_path});
|
|
try cwd.rename("api", api_dest);
|
|
}
|
|
}
|
|
};
|
|
|
|
const MakeDirStep = struct {
|
|
step: std.Build.Step,
|
|
dir_path: []const u8,
|
|
|
|
pub fn create(b: *std.Build, dir_path: []const u8) *std.Build.Step {
|
|
const self = b.allocator.create(MakeDirStep) catch @panic("OOM");
|
|
self.* = .{
|
|
.step = std.Build.Step.init(.{
|
|
.id = .custom,
|
|
.name = b.fmt("mkdir {s}", .{dir_path}),
|
|
.owner = b,
|
|
.makeFn = make,
|
|
}),
|
|
.dir_path = dir_path,
|
|
};
|
|
return &self.step;
|
|
}
|
|
|
|
fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void {
|
|
_ = options;
|
|
const self: *MakeDirStep = @fieldParentPtr("step", step);
|
|
try std.fs.cwd().makePath(self.dir_path);
|
|
}
|
|
};
|
|
|
|
pub fn generateApi(b: *std.Build, parser_exe: *std.Build.Step.Compile, fetch_sdl_step: *std.Build.Step) void {
|
|
// Archive existing json/ and api/ 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);
|
|
|
|
const basedir = b.option([]const u8, "basedir", "Working directory for the parser to execute in");
|
|
|
|
// Create basedir if specified
|
|
const basedir_step = if (basedir) |dir| MakeDirStep.create(b, dir) else &wf.step;
|
|
if (basedir != null)
|
|
basedir_step.dependOn(&wf.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=api/{s}.zig", .{header_info.output}));
|
|
regenerate.addArg(timestamp_arg);
|
|
if (basedir) |dir| {
|
|
regenerate.addArg(b.fmt("--basedir={s}", .{dir}));
|
|
}
|
|
// regenerate.addArg(b.fmt("--output=api/{s}.zig --mocks=mocks/{s}.c", .{ header_info.output, header_info.output }));
|
|
regenerate.step.dependOn(basedir_step);
|
|
regenerate_step.dependOn(®enerate.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);
|
|
if (basedir) |dir| {
|
|
regenerateJson.addArg(b.fmt("--basedir={s}", .{dir}));
|
|
}
|
|
regenerateJson.step.dependOn(basedir_step);
|
|
regenerate_step.dependOn(®enerateJson.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);
|
|
}
|