updates to sdl-parserscrap

This commit is contained in:
peterino2 2026-03-07 10:41:05 -08:00
parent 51e25a8da0
commit cca2376228
2 changed files with 87 additions and 29 deletions

View File

@ -37,6 +37,14 @@ You can specify which repo to use with `-Dsdl-url=<repo-url>` the git refs will
`-Dofficial` will use libsdl org's github repo. `-Dofficial` will use libsdl org's github repo.
If you already have SDL headers locally, use `-DtargetDir=<path>` instead of fetching from git:
```
zig build generate -DtargetDir=D:/path/to/SDL
```
`targetDir` may point at an SDL checkout root (`include/SDL3`) or directly at a directory containing the SDL headers. Generated outputs go to `<targetDir>/api` and `<targetDir>/json`, and parser scratch directories (`tmp`, `debug`, `archive`) are rooted there as well.
## Parser Usage ## Parser Usage
building the parser for standalone use or in your own scripts building the parser for standalone use or in your own scripts

108
build.zig
View File

@ -1,5 +1,21 @@
const std = @import("std"); const std = @import("std");
fn resolveHeaderRoot(allocator: std.mem.Allocator, target_dir: []const u8) ![]const u8 {
const include_sdl3 = try std.fs.path.join(allocator, &.{ target_dir, "include", "SDL3" });
defer allocator.free(include_sdl3);
if (std.fs.cwd().access(include_sdl3, .{})) {
return try allocator.dupe(u8, "include/SDL3");
} else |_| {}
const sdl3_dir = try std.fs.path.join(allocator, &.{ target_dir, "SDL3" });
defer allocator.free(sdl3_dir);
if (std.fs.cwd().access(sdl3_dir, .{})) {
return try allocator.dupe(u8, "SDL3");
} else |_| {}
return try allocator.dupe(u8, ".");
}
fn addFetchSdlStep(b: *std.Build) *std.Build.Step { fn addFetchSdlStep(b: *std.Build) *std.Build.Step {
const default_sdl_url = "git@github.com:castholm/SDL.git"; const default_sdl_url = "git@github.com:castholm/SDL.git";
const official_sdl_url = "git@github.com:libsdl-org/SDL.git"; const official_sdl_url = "git@github.com:libsdl-org/SDL.git";
@ -47,8 +63,9 @@ fn addFetchSdlStep(b: *std.Build) *std.Build.Step {
const ArchiveStep = struct { const ArchiveStep = struct {
step: std.Build.Step, step: std.Build.Step,
base_dir: ?[]const u8,
pub fn create(b: *std.Build) *std.Build.Step { pub fn create(b: *std.Build, base_dir: ?[]const u8) *std.Build.Step {
const self = b.allocator.create(ArchiveStep) catch @panic("OOM"); const self = b.allocator.create(ArchiveStep) catch @panic("OOM");
self.* = .{ self.* = .{
.step = std.Build.Step.init(.{ .step = std.Build.Step.init(.{
@ -57,36 +74,58 @@ const ArchiveStep = struct {
.owner = b, .owner = b,
.makeFn = make, .makeFn = make,
}), }),
.base_dir = base_dir,
}; };
return &self.step; return &self.step;
} }
fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void { fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) !void {
_ = step;
_ = options; _ = options;
const self: *ArchiveStep = @fieldParentPtr("step", step);
const cwd = std.fs.cwd(); const cwd = std.fs.cwd();
const json_exists = if (cwd.access("json", .{})) true else |_| false; const json_path = if (self.base_dir) |base_dir|
const api_exists = if (cwd.access("api", .{})) true else |_| false; try std.fs.path.join(step.owner.allocator, &.{ base_dir, "json" })
else
try step.owner.allocator.dupe(u8, "json");
defer step.owner.allocator.free(json_path);
const api_path = if (self.base_dir) |base_dir|
try std.fs.path.join(step.owner.allocator, &.{ base_dir, "api" })
else
try step.owner.allocator.dupe(u8, "api");
defer step.owner.allocator.free(api_path);
const archive_root = if (self.base_dir) |base_dir|
try std.fs.path.join(step.owner.allocator, &.{ base_dir, "archive", "generate" })
else
try step.owner.allocator.dupe(u8, "archive/generate");
defer step.owner.allocator.free(archive_root);
const json_exists = if (cwd.access(json_path, .{})) true else |_| false;
const api_exists = if (cwd.access(api_path, .{})) true else |_| false;
if (!json_exists and !api_exists) return; if (!json_exists and !api_exists) return;
const timestamp = std.time.timestamp(); const timestamp = std.time.timestamp();
var buf: [64]u8 = undefined; const timestamp_dir = try std.fmt.allocPrint(step.owner.allocator, "{d}", .{timestamp});
const archive_path = try std.fmt.bufPrint(&buf, "archive/generate/{d}", .{timestamp}); defer step.owner.allocator.free(timestamp_dir);
const archive_path = try std.fs.path.join(step.owner.allocator, &.{ archive_root, timestamp_dir });
defer step.owner.allocator.free(archive_path);
try cwd.makePath(archive_path); try cwd.makePath(archive_path);
if (json_exists) { if (json_exists) {
var json_dest_buf: [128]u8 = undefined; const json_dest = try std.fs.path.join(step.owner.allocator, &.{ archive_path, "json" });
const json_dest = try std.fmt.bufPrint(&json_dest_buf, "{s}/json", .{archive_path}); defer step.owner.allocator.free(json_dest);
try cwd.rename("json", json_dest); try cwd.rename(json_path, json_dest);
} }
if (api_exists) { if (api_exists) {
var api_dest_buf: [128]u8 = undefined; const api_dest = try std.fs.path.join(step.owner.allocator, &.{ archive_path, "api" });
const api_dest = try std.fmt.bufPrint(&api_dest_buf, "{s}/api", .{archive_path}); defer step.owner.allocator.free(api_dest);
try cwd.rename("api", api_dest); try cwd.rename(api_path, api_dest);
} }
} }
}; };
@ -117,22 +156,29 @@ const MakeDirStep = struct {
}; };
pub fn generateApi(b: *std.Build, parser_exe: *std.Build.Step.Compile, fetch_sdl_step: *std.Build.Step) void { 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 target_dir = b.option([]const u8, "targetDir", "Parse SDL headers from an existing local directory and generate api/ there instead of fetching git");
const archive_step = ArchiveStep.create(b); const basedir = b.option([]const u8, "basedir", "Working directory for the parser to execute in");
fetch_sdl_step.dependOn(archive_step); const effective_basedir = target_dir orelse basedir;
const header_root_suffix = if (target_dir) |dir| resolveHeaderRoot(b.allocator, dir) catch @panic("OOM") else null;
// Write a build marker file after fetch to enable caching // Archive existing json/ and api/ directories before regenerating
const archive_step = ArchiveStep.create(b, effective_basedir);
const generation_root = if (target_dir != null) archive_step else fetch_sdl_step;
if (target_dir == null) {
fetch_sdl_step.dependOn(archive_step);
}
// Write a build marker file after the source selection step to enable caching
const timestamp = std.time.timestamp(); const timestamp = std.time.timestamp();
const wf = b.addWriteFiles(); const wf = b.addWriteFiles();
const marker_file = wf.add(".buildmarker", b.fmt("{d}", .{timestamp})); const marker_file = wf.add(".buildmarker", b.fmt("{d}", .{timestamp}));
_ = marker_file; _ = marker_file;
wf.step.dependOn(fetch_sdl_step); wf.step.dependOn(generation_root);
const basedir = b.option([]const u8, "basedir", "Working directory for the parser to execute in");
// Create basedir if specified // Create basedir if specified
const basedir_step = if (basedir) |dir| MakeDirStep.create(b, dir) else &wf.step; const basedir_step = if (effective_basedir) |dir| MakeDirStep.create(b, dir) else &wf.step;
if (basedir != null) if (effective_basedir != null)
basedir_step.dependOn(&wf.step); basedir_step.dependOn(&wf.step);
// All public SDL3 API headers (53 total) // All public SDL3 API headers (53 total)
@ -193,17 +239,21 @@ pub fn generateApi(b: *std.Build, parser_exe: *std.Build.Step.Compile, fetch_sdl
}; };
const regenerate_step = b.step("generate", "Regenerate bindings from SDL headers"); const regenerate_step = b.step("generate", "Regenerate bindings from SDL headers");
const header_root = if (target_dir) |dir|
const header_path = "sdl3/include/SDL3"; b.fmt("{s}/{s}", .{ dir, header_root_suffix.? })
else
"sdl3/include/SDL3";
const output_root = if (target_dir) |dir| b.fmt("{s}/api", .{dir}) else "api";
const json_root = if (target_dir) |dir| b.fmt("{s}/json", .{dir}) else "json";
const timestamp_arg = b.fmt("--timestamp={d}", .{timestamp}); const timestamp_arg = b.fmt("--timestamp={d}", .{timestamp});
for (headers_to_generate) |header_info| { for (headers_to_generate) |header_info| {
const regenerate = b.addRunArtifact(parser_exe); const regenerate = b.addRunArtifact(parser_exe);
regenerate.addFileArg(b.path(b.fmt("{s}/{s}", .{ header_path, header_info.header }))); regenerate.addArg(b.fmt("{s}/{s}", .{ header_root, header_info.header }));
regenerate.addArg(b.fmt("--output=api/{s}.zig", .{header_info.output})); regenerate.addArg(b.fmt("--output={s}/{s}.zig", .{ output_root, header_info.output }));
regenerate.addArg(timestamp_arg); regenerate.addArg(timestamp_arg);
if (basedir) |dir| { if (effective_basedir) |dir| {
regenerate.addArg(b.fmt("--basedir={s}", .{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.addArg(b.fmt("--output=api/{s}.zig --mocks=mocks/{s}.c", .{ header_info.output, header_info.output }));
@ -211,10 +261,10 @@ pub fn generateApi(b: *std.Build, parser_exe: *std.Build.Step.Compile, fetch_sdl
regenerate_step.dependOn(&regenerate.step); regenerate_step.dependOn(&regenerate.step);
const regenerateJson = b.addRunArtifact(parser_exe); const regenerateJson = b.addRunArtifact(parser_exe);
regenerateJson.addFileArg(b.path(b.fmt("{s}/{s}", .{ header_path, header_info.header }))); regenerateJson.addArg(b.fmt("{s}/{s}", .{ header_root, header_info.header }));
regenerateJson.addArg(b.fmt("--generate-json=json/{s}.json", .{header_info.output})); regenerateJson.addArg(b.fmt("--generate-json={s}/{s}.json", .{ json_root, header_info.output }));
regenerateJson.addArg(timestamp_arg); regenerateJson.addArg(timestamp_arg);
if (basedir) |dir| { if (effective_basedir) |dir| {
regenerateJson.addArg(b.fmt("--basedir={s}", .{dir})); regenerateJson.addArg(b.fmt("--basedir={s}", .{dir}));
} }
regenerateJson.step.dependOn(basedir_step); regenerateJson.step.dependOn(basedir_step);