diff --git a/build.zig b/build.zig index 93a8686..0ea08d7 100644 --- a/build.zig +++ b/build.zig @@ -1,17 +1,16 @@ const std = @import("std"); -const zbs = std.build; +const Build = std.Build; const fs = std.fs; const mem = std.mem; -pub fn build(b: *zbs.Builder) void { +pub fn build(b: *Build) void { const target = b.standardTargetOptions(.{}); - const mode = b.standardReleaseOptions(); + const optimize = b.standardOptimizeOption(.{}); - const scanner = ScanProtocolsStep.create(b); - const wayland = zbs.Pkg{ - .name = "wayland", - .source = .{ .generated = &scanner.result }, - }; + const scanner = Scanner.create(b, "src/scanner.zig"); + defer scanner.finish(); + + const wayland = b.createModule(.{ .source_file = scanner.result }); scanner.generate("wl_compositor", 1); scanner.generate("wl_shm", 1); @@ -19,37 +18,41 @@ pub fn build(b: *zbs.Builder) void { scanner.generate("wl_output", 1); inline for ([_][]const u8{ "globals", "list", "listener", "seats" }) |example| { - const exe = b.addExecutable(example, "example/" ++ example ++ ".zig"); - exe.setTarget(target); - exe.setBuildMode(mode); + const exe = b.addExecutable(.{ + .name = example, + .root_source_file = .{ .path = "example/" ++ example ++ ".zig" }, + .target = target, + .optimize = optimize, + }); - exe.step.dependOn(&scanner.step); - exe.addPackage(wayland); + exe.addModule("wayland", wayland); scanner.addCSource(exe); exe.linkLibC(); exe.linkSystemLibrary("wayland-client"); - exe.install(); + b.installArtifact(exe); } const test_step = b.step("test", "Run the tests"); { - const scanner_tests = b.addTest("src/scanner.zig"); - scanner_tests.setTarget(target); - scanner_tests.setBuildMode(mode); + const scanner_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/scanner.zig" }, + .target = target, + .optimize = optimize, + }); - scanner_tests.step.dependOn(&scanner.step); - scanner_tests.addPackage(wayland); + scanner_tests.addModule("wayland", wayland); test_step.dependOn(&scanner_tests.step); } { - const ref_all = b.addTest("src/ref_all.zig"); - ref_all.setTarget(target); - ref_all.setBuildMode(mode); + const ref_all = b.addTest(.{ + .root_source_file = .{ .path = "src/ref_all.zig" }, + .target = target, + .optimize = optimize, + }); - ref_all.step.dependOn(&scanner.step); - ref_all.addPackage(wayland); + ref_all.addModule("wayland", wayland); scanner.addCSource(ref_all); ref_all.linkLibC(); ref_all.linkSystemLibrary("wayland-client"); @@ -60,47 +63,70 @@ pub fn build(b: *zbs.Builder) void { } } -pub const ScanProtocolsStep = struct { - const scanner = @import("src/scanner.zig"); +pub const Scanner = struct { + run: *Build.Step.Run, + result: Build.FileSource, - builder: *zbs.Builder, - step: zbs.Step, - result: zbs.GeneratedFile, + /// Path to the system protocol directory, stored to avoid invoking pkg-config N times. + system_protocol_path: []const u8, - /// 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), + // TODO remove these when the workaround for zig issue #131 is no longer needed. + compiles: std.ArrayListUnmanaged(*Build.Step.Compile) = .{}, + protocols: std.ArrayListUnmanaged([]const u8) = .{}, - /// Artifacts requiring the C interface definitions to be linked in. - /// TODO remove this after Zig issue #131 is implemented. - artifacts: std.ArrayList(*zbs.LibExeObjStep), + pub fn create(b: *Build, path: []const u8) *Scanner { + const exe = b.addExecutable(.{ + .name = "zig-wayland-scanner", + .root_source_file = .{ .path = path }, + }); - pub fn create(builder: *zbs.Builder) *ScanProtocolsStep { - const ally = builder.allocator; - 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), - .artifacts = std.ArrayList(*zbs.LibExeObjStep).init(ally), + const run = b.addRunArtifact(exe); + + run.addArg("-o"); + const result = run.addOutputFileArg("wayland.zig"); + + const scanner = b.allocator.create(Scanner) catch @panic("OOM"); + scanner.* = .{ + .run = run, + .result = result, + .system_protocol_path = blk: { + const pc_output = b.exec(&.{ "pkg-config", "--variable=pkgdatadir", "wayland-protocols" }); + break :blk mem.trim(u8, pc_output, &std.ascii.whitespace); + }, }; - return self; + + { + const pc_output = b.exec(&.{ "pkg-config", "--variable=pkgdatadir", "wayland-scanner" }); + const wayland_dir = mem.trim(u8, pc_output, &std.ascii.whitespace); + const wayland_xml = fs.path.join(b.allocator, &.{ wayland_dir, "wayland.xml" }) catch @panic("OOM"); + + run.addArg("-i"); + run.addFileSourceArg(.{ .path = wayland_xml }); + } + + return scanner; } - /// 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 oom(); + /// Scan protocol xml provided by the wayland-protocols package + /// at the given relative path (e.g. "stable/xdg-shell/xdg-shell.xml") + pub fn addSystemProtocol(scanner: *Scanner, path: []const u8) void { + const b = scanner.run.step.owner; + const absolute_path = fs.path.join(b.allocator, &.{ scanner.system_protocol_path, path }) catch @panic("OOM"); + + scanner.run.addArg("-i"); + scanner.run.addFileSourceArg(.{ .path = absolute_path }); + + scanner.protocols.append(b.allocator, absolute_path) catch @panic("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 { - self.system_protocols.append(relative_path) catch oom(); + /// Scan the protocol xml at the given path + pub fn addCustomProtocol(scanner: *Scanner, path: []const u8) void { + const b = scanner.run.step.owner; + + scanner.run.addArg("-i"); + scanner.run.addFileSourceArg(.{ .path = path }); + + scanner.protocols.append(b.allocator, b.dupe(path)) catch @panic("OOM"); } /// Generate code for the given global interface at the given version, @@ -108,73 +134,41 @@ pub const ScanProtocolsStep = struct { /// If the version found in the protocol xml is less than the requested version, /// an error will be printed and code generation will fail. /// Code is always generated for wl_display, wl_registry, wl_callback, and wl_buffer. - pub fn generate(self: *ScanProtocolsStep, global_interface: []const u8, version: u32) void { - self.targets.append(.{ .name = global_interface, .version = version }) catch oom(); + pub fn generate(scanner: *Scanner, global_interface: []const u8, version: u32) void { + var buffer: [32]u8 = undefined; + const version_str = std.fmt.bufPrint(&buffer, "{}", .{version}) catch unreachable; + + scanner.run.addArgs(&.{ "-g", global_interface, version_str }); } - /// Add the necessary C source to the compilation unit. - /// Once https://github.com/ziglang/zig/issues/131 we can remove this. - pub fn addCSource(self: *ScanProtocolsStep, obj: *zbs.LibExeObjStep) void { - self.artifacts.append(obj) catch oom(); + /// Generate and add the necessary C source to the compilation unit. + /// Once https://github.com/ziglang/zig/issues/131 is resolved we can remove this. + pub fn addCSource(scanner: *Scanner, compile: *Build.Step.Compile) void { + scanner.compiles.append(scanner.run.step.owner.allocator, compile) catch @panic("OOM"); } - fn make(step: *zbs.Step) !void { - const self = @fieldParentPtr(ScanProtocolsStep, "step", step); - const ally = self.builder.allocator; + /// This only exists as we need to generate and link C code due to the fact that we can't + /// create self-referential static data in zig yet. + /// TODO remove this after https://github.com/ziglang/zig/issues/131 is implemented. + pub fn finish(scanner: *Scanner) void { + const b = scanner.run.step.owner; - 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); + for (scanner.protocols.items) |protocol| { + const cmd = b.addSystemCommand(&.{ "wayland-scanner", "private-code", protocol }); - const wayland_xml = try fs.path.join(ally, &[_][]const u8{ wayland_dir, "wayland.xml" }); - try self.protocol_paths.append(wayland_xml); + // Extension is .xml, so slice off the last 4 characters + const basename = fs.path.basename(protocol); + const basename_no_ext = basename[0..(basename.len - 4)]; + const out_name = mem.concat(b.allocator, u8, &.{ basename_no_ext, "-protocol.c" }) catch @panic("OOM"); - 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 c_source = cmd.addOutputFileArg(out_name); - const out_path = try fs.path.join(ally, &[_][]const u8{ self.builder.cache_root, "zig-wayland" }); - - var root_dir = try fs.cwd().openDir(self.builder.build_root, .{}); - defer root_dir.close(); - var out_dir = try root_dir.makeOpenPath(out_path, .{}); - defer out_dir.close(); - 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. - for (self.protocol_paths.items) |protocol_path| { - const code_path = self.getCodePath(protocol_path); - _ = try self.builder.exec( - &[_][]const u8{ "wayland-scanner", "private-code", protocol_path, code_path }, - ); - for (self.artifacts.items) |artifact| { - artifact.addCSourceFile(code_path, &[_][]const u8{"-std=c99"}); + for (scanner.compiles.items) |compile| { + compile.addCSourceFileSource(.{ + .source = c_source, + .args = &.{ "-std=c99", "-O2" }, + }); } } - - self.result.path = try fs.path.join(ally, &[_][]const u8{ out_path, "wayland.zig" }); - } - - fn getCodePath(self: *ScanProtocolsStep, xml_in_path: []const u8) []const u8 { - const ally = self.builder.allocator; - // 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 oom(); - return fs.path.join(ally, &[_][]const u8{ - self.builder.build_root, - self.builder.cache_root, - "zig-wayland", - code_filename, - }) catch oom(); } }; - -fn oom() noreturn { - @panic("out of memory"); -} diff --git a/src/common_core.zig b/src/common_core.zig index 84213a8..5a3b914 100644 --- a/src/common_core.zig +++ b/src/common_core.zig @@ -83,18 +83,18 @@ pub fn Dispatcher(comptime Obj: type, comptime Data: type) type { _: *const Message, args: [*]Argument, ) callconv(.C) c_int { - inline for (@typeInfo(Payload).Union.fields) |payload_field, payload_num| { + inline for (@typeInfo(Payload).Union.fields, 0..) |payload_field, payload_num| { if (payload_num == opcode) { - var payload_data: payload_field.field_type = undefined; - if (payload_field.field_type != void) { - inline for (@typeInfo(payload_field.field_type).Struct.fields) |f, i| { - switch (@typeInfo(f.field_type)) { + var payload_data: payload_field.type = undefined; + if (payload_field.type != void) { + inline for (@typeInfo(payload_field.type).Struct.fields, 0..) |f, i| { + switch (@typeInfo(f.type)) { // signed/unsigned ints, fds, new_ids, bitfield enums - .Int, .Struct => @field(payload_data, f.name) = @bitCast(f.field_type, args[i].u), + .Int, .Struct => @field(payload_data, f.name) = @bitCast(f.type, args[i].u), // objects, strings, arrays - .Pointer, .Optional => @field(payload_data, f.name) = @intToPtr(f.field_type, @ptrToInt(args[i].o)), + .Pointer, .Optional => @field(payload_data, f.name) = @intToPtr(f.type, @ptrToInt(args[i].o)), // non-bitfield enums - .Enum => @field(payload_data, f.name) = @intToEnum(f.field_type, args[i].i), + .Enum => @field(payload_data, f.name) = @intToEnum(f.type, args[i].i), else => unreachable, } } diff --git a/src/scanner.zig b/src/scanner.zig index 0eecc05..443529a 100644 --- a/src/scanner.zig +++ b/src/scanner.zig @@ -23,15 +23,48 @@ pub const Target = struct { version: u32, }; -pub fn scan( - root_dir: fs.Dir, - out_dir: fs.Dir, +pub fn main() !void { + defer assert(general_purpose_allocator.deinit() == .ok); + + var protocols = std.ArrayList([]const u8).init(gpa); + defer protocols.deinit(); + + var targets = std.ArrayList(Target).init(gpa); + defer targets.deinit(); + + var out_path_opt: ?[]const u8 = null; + + var args = std.process.args(); + + while (args.next()) |arg| { + if (mem.eql(u8, arg, "-i")) { + const protocol_path = args.next() orelse return error.MissingArg; + try protocols.append(protocol_path); + } else if (mem.eql(u8, arg, "-g")) { + const name = args.next() orelse return error.MissingArg; + const version = args.next() orelse return error.MissingArg; + try targets.append(.{ + .name = name, + .version = try std.fmt.parseInt(u32, version, 10), + }); + } else if (mem.eql(u8, arg, "-o")) { + out_path_opt = args.next() orelse return error.MissingArg; + } + } + + const out_path = out_path_opt orelse return error.MissingArg; + try scan(out_path, protocols.items, targets.items); +} + +fn scan( + out_path: []const u8, protocols: []const []const u8, targets: []const Target, ) !void { - defer assert(!general_purpose_allocator.deinit()); + const out_dir_path = fs.path.dirname(out_path) orelse return error.InvalidOutPath; + const out_dir = try fs.cwd().openDir(out_dir_path, .{}); - const wayland_file = try out_dir.createFile("wayland.zig", .{}); + const wayland_file = try out_dir.createFile(fs.path.basename(out_path), .{}); try wayland_file.writeAll( \\pub const client = @import("client.zig"); \\pub const server = @import("server.zig"); @@ -42,7 +75,7 @@ pub fn scan( defer scanner.deinit(); for (protocols) |xml_path| { - try scanner.scanProtocol(root_dir, out_dir, xml_path); + try scanner.scanProtocol(out_dir, xml_path); } if (scanner.remaining_targets.items.len != 0) { @@ -148,8 +181,8 @@ const Scanner = struct { map.deinit(); } - fn scanProtocol(scanner: *Scanner, root_dir: fs.Dir, out_dir: fs.Dir, xml_path: []const u8) !void { - const xml_file = try root_dir.openFile(xml_path, .{}); + fn scanProtocol(scanner: *Scanner, out_dir: fs.Dir, xml_path: []const u8) !void { + const xml_file = try fs.cwd().openFile(xml_path, .{}); defer xml_file.close(); var arena = std.heap.ArenaAllocator.init(gpa); @@ -161,7 +194,7 @@ const Scanner = struct { os.exit(1); }; - var buffered_writer: std.io.BufferedWriter(4096, std.fs.File.Writer) = .{ + var buffered_writer: std.io.BufferedWriter(4096, fs.File.Writer) = .{ .unbuffered_writer = undefined, }; { @@ -404,14 +437,14 @@ const Protocol = struct { if (protocol.copyright) |copyright| { var it = mem.split(u8, copyright, "\n"); while (it.next()) |line| { - try writer.print("// {s}\n", .{mem.trim(u8, line, &std.ascii.spaces)}); + try writer.print("// {s}\n", .{mem.trim(u8, line, &std.ascii.whitespace)}); } try writer.writeByte('\n'); } if (protocol.toplevel_description) |toplevel_description| { var it = mem.split(u8, toplevel_description, "\n"); while (it.next()) |line| { - try writer.print("// {s}\n", .{mem.trim(u8, line, &std.ascii.spaces)}); + try writer.print("// {s}\n", .{mem.trim(u8, line, &std.ascii.whitespace)}); } try writer.writeByte('\n'); } @@ -555,7 +588,7 @@ const Interface = struct { \\ pub const generated_version = {[version]}; \\ pub const getInterface = common.{[namespace]}.{[interface]}.getInterface; , .{ - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), .version = std.math.min(interface.version, target_version), .namespace = fmtId(namespace), .interface = fmtId(trimPrefix(interface.name)), @@ -564,7 +597,7 @@ const Interface = struct { for (interface.enums) |e| { if (e.since <= target_version) { try writer.print("pub const {[type]} = common.{[namespace]}.{[interface]}.{[type]};\n", .{ - .@"type" = titleCase(e.name), + .type = titleCase(e.name), .namespace = fmtId(namespace), .interface = fmtId(trimPrefix(interface.name)), }); @@ -579,7 +612,7 @@ const Interface = struct { \\}} , .{ .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), }); const has_event = for (interface.events) |event| { @@ -607,12 +640,12 @@ const Interface = struct { \\}} , .{ .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), }); } var has_destroy = false; - for (interface.requests) |request, opcode| { + for (interface.requests, 0..) |request, opcode| { if (request.since <= target_version) { if (mem.eql(u8, request.name, "destroy")) has_destroy = true; try request.emitFn(side, writer, interface, opcode); @@ -629,7 +662,7 @@ const Interface = struct { \\}} , .{ .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), }); } } else { @@ -642,7 +675,7 @@ const Interface = struct { \\ return @ptrCast(*{[type]}, server.wl.Resource.fromLink(_link)); \\}} , .{ - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), .interface = fmtId(trimPrefix(interface.name)), }); @@ -660,7 +693,7 @@ const Interface = struct { , .{ .function = camelCase(func[0]), .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), .return_type = camelCase(func[1]), }); @@ -674,7 +707,7 @@ const Interface = struct { \\}} , .{ .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), }); } @@ -716,7 +749,7 @@ const Interface = struct { \\}} , .{ .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), }); } else { try writer.print( @@ -743,11 +776,11 @@ const Interface = struct { \\}} , .{ .interface = fmtId(trimPrefix(interface.name)), - .@"type" = titleCaseTrim(interface.name), + .type = titleCaseTrim(interface.name), }); } - for (interface.events) |event, opcode| { + for (interface.events, 0..) |event, opcode| { if (event.since <= target_version) { try event.emitFn(side, writer, interface, opcode); } diff --git a/src/wayland_server_core.zig b/src/wayland_server_core.zig index 267cc97..fa90645 100644 --- a/src/wayland_server_core.zig +++ b/src/wayland_server_core.zig @@ -550,13 +550,13 @@ pub fn Listener(comptime T: type) type { self.notify = if (T == void) struct { fn wrapper(listener: *Self, _: ?*anyopaque) callconv(.C) void { - @call(.{ .modifier = .always_inline }, notify, .{listener}); + @call(.always_inline, notify, .{listener}); } }.wrapper else struct { fn wrapper(listener: *Self, data: ?*anyopaque) callconv(.C) void { - @call(.{ .modifier = .always_inline }, notify, .{ listener, @intToPtr(T, @ptrToInt(data)) }); + @call(.always_inline, notify, .{ listener, @intToPtr(T, @ptrToInt(data)) }); } }.wrapper; }