mirror of
https://github.com/zoriya/zig-wayland.git
synced 2026-06-01 10:25:07 +00:00
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:
@@ -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.
|
||||
|
||||
@@ -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
@@ -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| {
|
||||
|
||||
Reference in New Issue
Block a user