Files
zig-wayland/build.zig

185 lines
6.8 KiB
Zig

const std = @import("std");
const Build = std.Build;
const fs = std.fs;
const mem = std.mem;
pub fn build(b: *Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const scanner = Scanner.create(b, .{});
const wayland = b.createModule(.{ .source_file = scanner.result });
scanner.generate("wl_compositor", 1);
scanner.generate("wl_shm", 1);
scanner.generate("wl_seat", 2);
scanner.generate("wl_output", 1);
inline for ([_][]const u8{ "globals", "list", "listener", "seats" }) |example| {
const exe = b.addExecutable(.{
.name = example,
.root_source_file = .{ .path = "example/" ++ example ++ ".zig" },
.target = target,
.optimize = optimize,
});
exe.addModule("wayland", wayland);
scanner.addCSource(exe);
exe.linkLibC();
exe.linkSystemLibrary("wayland-client");
b.installArtifact(exe);
}
const test_step = b.step("test", "Run the tests");
{
const scanner_tests = b.addTest(.{
.root_source_file = .{ .path = "src/scanner.zig" },
.target = target,
.optimize = optimize,
});
scanner_tests.addModule("wayland", wayland);
test_step.dependOn(&scanner_tests.step);
}
{
const ref_all = b.addTest(.{
.root_source_file = .{ .path = "src/ref_all.zig" },
.target = target,
.optimize = optimize,
});
ref_all.addModule("wayland", wayland);
scanner.addCSource(ref_all);
ref_all.linkLibC();
ref_all.linkSystemLibrary("wayland-client");
ref_all.linkSystemLibrary("wayland-server");
ref_all.linkSystemLibrary("wayland-egl");
ref_all.linkSystemLibrary("wayland-cursor");
test_step.dependOn(&ref_all.step);
}
}
pub const Scanner = struct {
run: *Build.Step.Run,
result: Build.LazyPath,
/// Path to the system protocol directory, stored to avoid invoking pkg-config N times.
wayland_protocols_path: []const u8,
// TODO remove these when the workaround for zig issue #131 is no longer needed.
compiles: std.ArrayListUnmanaged(*Build.Step.Compile) = .{},
c_sources: std.ArrayListUnmanaged(Build.LazyPath) = .{},
pub const Options = struct {
/// Path to the wayland.xml file.
/// If null, the output of `pkg-config --variable=pkgdatadir wayland-scanner` will be used.
wayland_xml_path: ?[]const u8 = null,
/// Path to the wayland-protocols installation.
/// If null, the output of `pkg-config --variable=pkgdatadir wayland-protocols` will be used.
wayland_protocols_path: ?[]const u8 = null,
};
pub fn create(b: *Build, options: Options) *Scanner {
const wayland_xml_path = options.wayland_xml_path orelse blk: {
const pc_output = b.run(&.{ "pkg-config", "--variable=pkgdatadir", "wayland-scanner" });
break :blk b.pathJoin(&.{ mem.trim(u8, pc_output, &std.ascii.whitespace), "wayland.xml" });
};
const wayland_protocols_path = options.wayland_protocols_path orelse blk: {
const pc_output = b.run(&.{ "pkg-config", "--variable=pkgdatadir", "wayland-protocols" });
break :blk mem.trim(u8, pc_output, &std.ascii.whitespace);
};
const zig_wayland_dir = fs.path.dirname(@src().file) orelse ".";
const exe = b.addExecutable(.{
.name = "zig-wayland-scanner",
.root_source_file = .{ .path = b.pathJoin(&.{ zig_wayland_dir, "src/scanner.zig" }) },
});
const run = b.addRunArtifact(exe);
run.addArg("-o");
const result = run.addOutputFileArg("wayland.zig");
run.addArg("-i");
run.addFileArg(.{ .path = wayland_xml_path });
const scanner = b.allocator.create(Scanner) catch @panic("OOM");
scanner.* = .{
.run = run,
.result = result,
.wayland_protocols_path = wayland_protocols_path,
};
return scanner;
}
/// Scan protocol xml provided by the wayland-protocols package at the given path
/// relative to the wayland-protocols installation. (e.g. "stable/xdg-shell/xdg-shell.xml")
pub fn addSystemProtocol(scanner: *Scanner, relative_path: []const u8) void {
const b = scanner.run.step.owner;
const full_path = b.pathJoin(&.{ scanner.wayland_protocols_path, relative_path });
scanner.run.addArg("-i");
scanner.run.addFileArg(.{ .path = full_path });
scanner.generateCSource(full_path);
}
/// Scan the protocol xml at the given path.
pub fn addCustomProtocol(scanner: *Scanner, path: []const u8) void {
scanner.run.addArg("-i");
scanner.run.addFileArg(.{ .path = path });
scanner.generateCSource(path);
}
/// Generate code for the given global interface at the given version,
/// as well as all interfaces that can be created using it at that version.
/// 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(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 });
}
/// 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 {
const b = scanner.run.step.owner;
for (scanner.c_sources.items) |c_source| {
compile.addCSourceFile(.{
.file = c_source,
.flags = &.{ "-std=c99", "-O2" },
});
}
scanner.compiles.append(b.allocator, compile) catch @panic("OOM");
}
/// Once https://github.com/ziglang/zig/issues/131 is resolved we can remove this.
fn generateCSource(scanner: *Scanner, protocol: []const u8) void {
const b = scanner.run.step.owner;
const cmd = b.addSystemCommand(&.{ "wayland-scanner", "private-code", protocol });
const out_name = mem.concat(b.allocator, u8, &.{ fs.path.stem(protocol), "-protocol.c" }) catch @panic("OOM");
const c_source = cmd.addOutputFileArg(out_name);
for (scanner.compiles.items) |compile| {
compile.addCSourceFile(.{
.file = c_source,
.flags = &.{ "-std=c99", "-O2" },
});
}
scanner.c_sources.append(b.allocator, c_source) catch @panic("OOM");
}
};