Update to Zig master

This commit is contained in:
Isaac Freund
2023-06-14 14:31:06 +02:00
parent d507189f62
commit 8429629cb5
4 changed files with 174 additions and 147 deletions
+108 -114
View File
@@ -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");
}
+8 -8
View File
@@ -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,
}
}
+56 -23
View File
@@ -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);
}
+2 -2
View File
@@ -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;
}