scanner: improve error handling

Invalid protocol xml will still give poor error messages, but that won't
be fixed until there exists a more featureful xml parser for zig which
can report token line/column numbers and whatnot. Until then using
wayland-scanner to validate protocol xml is recommended.
This commit is contained in:
Isaac Freund
2022-05-11 14:58:26 +02:00
parent f8c1dcf05f
commit 5336fea583
3 changed files with 37 additions and 21 deletions
+4
View File
@@ -63,6 +63,10 @@ const wl = wayland.client.wl;
There is an example project using zig-wayland here:
[hello-zig-wayland](https://github.com/ifreund/hello-zig-wayland).
Note that zig-wayland does not currently do extensive verification of Wayland
protocol xml or provide good error messages if protocol xml is invalid. It is
recommend to use `wayland-scanner --strict` to debug protocol xml instead.
## License
zig-wayland is released under the MIT (expat) license.
+26 -15
View File
@@ -67,39 +67,36 @@ pub const ScanProtocolsStep = struct {
step: zbs.Step,
result: zbs.GeneratedFile,
/// Slice of absolute paths of protocol xml files to be scanned
/// Absolute paths to protocol xml
protocol_paths: std.ArrayList([]const u8),
/// Paths relative to the system wayland-protocol directory
system_protocols: std.ArrayList([]const u8),
targets: std.ArrayList(scanner.Target),
pub fn create(builder: *zbs.Builder) *ScanProtocolsStep {
const ally = builder.allocator;
const self = ally.create(ScanProtocolsStep) catch unreachable;
const self = ally.create(ScanProtocolsStep) catch oom();
self.* = .{
.builder = builder,
.step = zbs.Step.init(.custom, "Scan Protocols", ally, make),
.result = .{ .step = &self.step, .path = null },
.protocol_paths = std.ArrayList([]const u8).init(ally),
.system_protocols = std.ArrayList([]const u8).init(ally),
.targets = std.ArrayList(scanner.Target).init(ally),
};
self.targets.append(.{ .name = "wl_display", .version = 1 }) catch unreachable;
self.targets.append(.{ .name = "wl_display", .version = 1 }) catch oom();
return self;
}
/// Scan the protocol xml at the given absolute or relative path
pub fn addProtocolPath(self: *ScanProtocolsStep, path: []const u8) void {
self.protocol_paths.append(path) catch unreachable;
self.protocol_paths.append(path) catch oom();
}
/// Scan the protocol xml provided by the wayland-protocols
/// package given the relative path (e.g. "stable/xdg-shell/xdg-shell.xml")
pub fn addSystemProtocol(self: *ScanProtocolsStep, relative_path: []const u8) void {
const protocol_dir = mem.trim(u8, self.builder.exec(
&[_][]const u8{ "pkg-config", "--variable=pkgdatadir", "wayland-protocols" },
) catch unreachable, &std.ascii.spaces);
self.addProtocolPath(fs.path.join(
self.builder.allocator,
&[_][]const u8{ protocol_dir, relative_path },
) catch unreachable);
self.system_protocols.append(relative_path) catch oom();
}
/// Generate code for the given global interface at the given version,
@@ -108,7 +105,7 @@ pub const ScanProtocolsStep = struct {
/// an error will be printed and code generation will fail.
/// Code is always generated for wl_display, wl_registry, and wl_callback.
pub fn generate(self: *ScanProtocolsStep, global_interface: []const u8, version: u32) void {
self.targets.append(.{ .name = global_interface, .version = version }) catch unreachable;
self.targets.append(.{ .name = global_interface, .version = version }) catch oom();
}
fn make(step: *zbs.Step) !void {
@@ -118,7 +115,17 @@ pub const ScanProtocolsStep = struct {
const wayland_dir = mem.trim(u8, try self.builder.exec(
&[_][]const u8{ "pkg-config", "--variable=pkgdatadir", "wayland-scanner" },
), &std.ascii.spaces);
const wayland_protocols_dir = mem.trim(u8, try self.builder.exec(
&[_][]const u8{ "pkg-config", "--variable=pkgdatadir", "wayland-protocols" },
), &std.ascii.spaces);
const wayland_xml = try fs.path.join(ally, &[_][]const u8{ wayland_dir, "wayland.xml" });
try self.protocol_paths.append(wayland_xml);
for (self.system_protocols.items) |relative_path| {
const absolute_path = try fs.path.join(ally, &[_][]const u8{ wayland_protocols_dir, relative_path });
try self.protocol_paths.append(absolute_path);
}
const out_path = try fs.path.join(ally, &[_][]const u8{ self.builder.cache_root, "zig-wayland" });
@@ -126,7 +133,7 @@ pub const ScanProtocolsStep = struct {
defer root_dir.close();
var out_dir = try root_dir.makeOpenPath(out_path, .{});
defer out_dir.close();
try scanner.scan(root_dir, out_dir, wayland_xml, self.protocol_paths.items, self.targets.items);
try scanner.scan(root_dir, out_dir, self.protocol_paths.items, self.targets.items);
// Once https://github.com/ziglang/zig/issues/131 is implemented
// we can stop generating/linking C code.
@@ -151,12 +158,16 @@ pub const ScanProtocolsStep = struct {
// Extension is .xml, so slice off the last 4 characters
const basename = fs.path.basename(xml_in_path);
const basename_no_ext = basename[0..(basename.len - 4)];
const code_filename = std.fmt.allocPrint(ally, "{s}-protocol.c", .{basename_no_ext}) catch unreachable;
const code_filename = std.fmt.allocPrint(ally, "{s}-protocol.c", .{basename_no_ext}) catch oom();
return fs.path.join(ally, &[_][]const u8{
self.builder.build_root,
self.builder.cache_root,
"zig-wayland",
code_filename,
}) catch unreachable;
}) catch oom();
}
};
fn oom() noreturn {
@panic("out of memory");
}
+7 -6
View File
@@ -2,8 +2,11 @@ const std = @import("std");
const assert = std.debug.assert;
const fs = std.fs;
const mem = std.mem;
const os = std.os;
const fmtId = std.zig.fmtId;
const log = std.log.scoped(.@"zig-wayland");
const xml = @import("xml.zig");
const gpa = general_purpose_allocator.allocator();
@@ -23,7 +26,6 @@ pub const Target = struct {
pub fn scan(
root_dir: fs.Dir,
out_dir: fs.Dir,
wayland_xml: []const u8,
protocols: []const []const u8,
targets: []const Target,
) !void {
@@ -39,16 +41,15 @@ pub fn scan(
var scanner = try Scanner.init(targets);
defer scanner.deinit();
try scanner.scanProtocol(root_dir, out_dir, wayland_xml);
for (protocols) |xml_path| {
try scanner.scanProtocol(root_dir, out_dir, xml_path);
}
if (scanner.remaining_targets.items.len != 0) {
std.log.err("requested global interface '{s}' not found in provided protocol xml", .{
log.err("requested global interface '{s}' not found in provided protocol xml", .{
scanner.remaining_targets.items[0].name,
});
return error.GlobalNotFound;
os.exit(1);
}
{
@@ -410,12 +411,12 @@ const Protocol = struct {
for (protocol.globals) |global| {
if (mem.eql(u8, target.name, global.interface.name)) {
if (global.interface.version < target.version) {
std.log.err("requested {s} version {d} but only version {d} is available", .{
log.err("requested {s} version {d} but only version {d} is available in provided xml", .{
target.name,
target.version,
global.interface.version,
});
return error.VersionMismatch;
os.exit(1);
}
try global.interface.emit(.client, target.version, protocol.namespace, writer);
for (global.children) |child| {