diff --git a/generator/main.zig b/generator/main.zig index 93d20b5..912b4b1 100644 --- a/generator/main.zig +++ b/generator/main.zig @@ -1,23 +1,19 @@ const std = @import("std"); const generator = @import("vulkan/generator.zig"); -const usage = - \\Usage: {s} [options] - \\Options: - \\-h --help show this message and exit. - \\-a --api Generate API for 'vulkan' or 'vulkansc'. Defaults to 'vulkan'. - \\ -; - -pub fn main() !void { - const stderr = std.io.getStdErr(); +fn invalidUsage(prog_name: []const u8, comptime fmt: []const u8, args: anytype) noreturn { + std.log.err(fmt, args); + std.log.err("see {s} --help for usage", .{prog_name}); + std.process.exit(1); +} +pub fn main() void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); var args = try std.process.argsWithAllocator(allocator); - const prog_name = args.next() orelse return error.ExecutableNameMissing; + const prog_name = args.next() orelse "vulkan-zig-generator"; var maybe_xml_path: ?[]const u8 = null; var maybe_out_path: ?[]const u8 = null; @@ -26,7 +22,7 @@ pub fn main() !void { while (args.next()) |arg| { if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { @setEvalBranchQuota(2000); - try stderr.writer().print( + std.io.getStdOut().writer().print( \\Utility to generate a Zig binding from the Vulkan XML API registry. \\ \\The most recent Vulkan XML API registry can be obtained from @@ -34,64 +30,98 @@ pub fn main() !void { \\and the most recent LunarG Vulkan SDK version can be found at \\$VULKAN_SDK/x86_64/share/vulkan/registry/vk.xml. \\ + \\Usage: {s} [options] + \\Options: + \\-h --help show this message and exit. + \\-a --api Generate API for 'vulkan' or 'vulkansc'. Defaults to 'vulkan'. \\ - ++ usage, + , .{prog_name}, - ); + ) catch |err| { + std.log.err("failed to write to stdout: {s}", .{@errorName(err)}); + std.process.exit(1); + }; return; } else if (std.mem.eql(u8, arg, "-a") or std.mem.eql(u8, arg, "--api")) { const api_str = args.next() orelse { - try stderr.writer().print("Error: {s} expects argument \n", .{arg}); - return; + invalidUsage(prog_name, "{s} expects argument ", .{arg}); }; api = std.meta.stringToEnum(generator.Api, api_str) orelse { - try stderr.writer().print("Error: Invalid api '{s}'", .{api_str}); - return; + invalidUsage(prog_name, "invalid api '{s}'", .{api_str}); }; } else if (maybe_xml_path == null) { maybe_xml_path = arg; } else if (maybe_out_path == null) { maybe_out_path = arg; } else { - try stderr.writer().print("Error: Superficial argument '{s}'\n", .{arg}); - return; + invalidUsage(prog_name, "superficial argument '{s}'", .{arg}); } } const xml_path = maybe_xml_path orelse { - try stderr.writer().print("Error: Missing required argument \n" ++ usage, .{prog_name}); - return; + invalidUsage(prog_name, "missing required argument ", .{}); }; const out_path = maybe_out_path orelse { - try stderr.writer().print("Error: Missing required argument \n" ++ usage, .{prog_name}); - return; + invalidUsage(prog_name, "missing required argument ", .{}); }; const cwd = std.fs.cwd(); const xml_src = cwd.readFileAlloc(allocator, xml_path, std.math.maxInt(usize)) catch |err| { - try stderr.writer().print("Error: Failed to open input file '{s}' ({s})\n", .{ xml_path, @errorName(err) }); - return; + std.log.err("failed to open input file '{s}' ({s})", .{ xml_path, @errorName(err) }); + std.process.exit(1); }; var out_buffer = std.ArrayList(u8).init(allocator); - try generator.generate(allocator, api, xml_src, out_buffer.writer()); - try out_buffer.append(0); + generator.generate(allocator, api, xml_src, out_buffer.writer()) catch |err| switch (err) { + error.InvalidXml => { + std.log.err("invalid vulkan registry - invalid xml", .{}); + std.log.err("please check that the correct vk.xml file is passed", .{}); + std.process.exit(1); + }, + error.InvalidRegistry => { + std.log.err("invalid vulkan registry - registry is valid xml but contents are invalid", .{}); + std.log.err("please check that the correct vk.xml file is passed", .{}); + std.process.exit(1); + }, + error.UnhandledBitfieldStruct => { + std.log.err("unhandled struct with bit fields detected in vk.xml", .{}); + std.log.err("this is a bug in vulkan-zig", .{}); + std.log.err("please make a bug report at https://github.com/Snektron/vulkan-zig/issues/", .{}); + std.process.exit(1); + }, + error.OutOfMemory => @panic("oom"), + }; + + out_buffer.append(0) catch @panic("oom"); const src = out_buffer.items[0 .. out_buffer.items.len - 1 :0]; - const tree = try std.zig.Ast.parse(allocator, src, .zig); - const formatted = try tree.render(allocator); + const tree = std.zig.Ast.parse(allocator, src, .zig) catch |err| switch (err) { + error.OutOfMemory => @panic("oom"), + }; + + if (tree.errors.len > 0) { + std.log.err("generated invalid zig code", .{}); + std.log.err("this is a bug in vulkan-zig", .{}); + std.log.err("please make a bug report at https://github.com/Snektron/vulkan-zig/issues/", .{}); + + std.process.exit(1); + } + + const formatted = tree.render(allocator) catch |err| switch (err) { + error.OutOfMemory => @panic("oom"), + }; defer allocator.free(formatted); if (std.fs.path.dirname(out_path)) |dir| { cwd.makePath(dir) catch |err| { - try stderr.writer().print("Error: Failed to create output directory '{s}' ({s})\n", .{ dir, @errorName(err) }); - return; + std.log.err("failed to create output directory '{s}' ({s})", .{ dir, @errorName(err) }); + std.process.exit(1); }; } cwd.writeFile(out_path, formatted) catch |err| { - try stderr.writer().print("Error: Failed to write to output file '{s}' ({s})\n", .{ out_path, @errorName(err) }); - return; + std.log.err("failed to write to output file '{s}' ({s})", .{ out_path, @errorName(err) }); + std.process.exit(1); }; } diff --git a/generator/vulkan/parse.zig b/generator/vulkan/parse.zig index d95c65f..8491b81 100644 --- a/generator/vulkan/parse.zig +++ b/generator/vulkan/parse.zig @@ -300,7 +300,6 @@ fn lenToPointer(fields: Fields, len: []const u8) std.meta.Tuple(&.{ registry.Poi } fn parsePointerMeta(fields: Fields, type_info: *registry.TypeInfo, elem: *xml.Element) !void { - var len_attribute_depth: usize = 0; if (elem.getAttribute("len")) |lens| { @@ -327,7 +326,6 @@ fn parsePointerMeta(fields: Fields, type_info: *registry.TypeInfo, elem: *xml.El } } - var current_depth: usize = 0; if (elem.getAttribute("optional")) |optionals| { @@ -342,8 +340,7 @@ fn parsePointerMeta(fields: Fields, type_info: *registry.TypeInfo, elem: *xml.El is_already_optional = current_type_info.pointer.is_optional; current_type_info.pointer.is_optional = - is_already_optional or mem.eql(u8, optional_str, "true"); - + is_already_optional or mem.eql(u8, optional_str, "true"); } else { // There is no information for this pointer, probably incorrect. // Currently there is one definition where this is the case, VkCudaLaunchInfoNV.