diff --git a/README.md b/README.md index 77439ec..04cb21b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,11 @@ development environment: ## Running the binding generator +Running the binding generator requires GIR files to process. The easiest way to +get the full set of required GIR files is to set up a Flatpak development +environment as described in the previous section. Otherwise, a custom set of +bindings can be built by running the `zig-gobject` binary directly. + To generate all available bindings using the files under `lib/gir-files`, run `zig build codegen`. This will generate bindings to the `bindings` directory, which can be used as a dependency (using the Zig package manager) in other diff --git a/build.zig b/build.zig index 1ff5975..a3221ab 100644 --- a/build.zig +++ b/build.zig @@ -187,8 +187,8 @@ fn addCodegenStep(b: *std.Build, codegen_exe: *std.Build.Step.Compile) !*std.Bui const codegen_cmd = b.addRunArtifact(codegen_exe); codegen_cmd.addArgs(&.{ "--gir-dir", try b.build_root.join(b.allocator, &.{"gir-overrides"}) }); - codegen_cmd.addArg("--gir-dir"); - codegen_cmd.addDirectoryArg(b.dependency("gir", .{}).path(".")); + const gir_files_path = b.option([]const u8, "gir-files-path", "Path to GIR files") orelse "/usr/share/gir-1.0"; + codegen_cmd.addArgs(&.{ "--gir-dir", gir_files_path }); codegen_cmd.addArgs(&.{ "--bindings-dir", try b.build_root.join(b.allocator, &.{"binding-overrides"}) }); codegen_cmd.addArgs(&.{ "--extensions-dir", try b.build_root.join(b.allocator, &.{"extensions"}) }); codegen_cmd.addArgs(&.{ "--output-dir", try b.build_root.join(b.allocator, &.{"bindings"}) }); diff --git a/build.zig.zon b/build.zig.zon index 5e1198f..49931e5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -9,10 +9,6 @@ "build.zig.zon", }, .dependencies = .{ - .gir = .{ - .url = "git+https://github.com/ianprime0509/gir-files#d0d06238bc0f1759b450cc7113f8fc3637c6533d", - .hash = "1220d03206a277aa5adac91667c7ff7236017fde65c2034bdeec3203cfca09a7152e", - }, .xml = .{ .url = "git+https://github.com/ianprime0509/zig-xml#5d995ef95bb5796f38bf16c0d4750cba06d86549", .hash = "1220d70515390ed942a0864fe3aad2f3b485b3fcb9df07d61dbefea9873373647e33", diff --git a/src/gir.zig b/src/gir.zig index 01ae683..230050f 100644 --- a/src/gir.zig +++ b/src/gir.zig @@ -3,16 +3,26 @@ const xml = @import("xml"); const mem = std.mem; const Allocator = mem.Allocator; -pub const FindError = error{ InvalidGir, RepositoryNotFound } || Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || error{ - FileSystem, - InputOutput, - NotSupported, - Unseekable, +pub const Diagnostics = struct { + errors: std.ArrayListUnmanaged([]u8) = .{}, + allocator: Allocator, + + pub fn deinit(diag: *Diagnostics) void { + for (diag.errors.items) |err| diag.allocator.free(err); + diag.errors.deinit(diag.allocator); + diag.* = undefined; + } + + fn add(diag: *Diagnostics, comptime fmt: []const u8, args: anytype) Allocator.Error!void { + const formatted = try std.fmt.allocPrint(diag.allocator, fmt, args); + errdefer diag.allocator.free(formatted); + try diag.errors.append(diag.allocator, formatted); + } }; /// Finds and parses all repositories for the given root libraries, transitively /// including dependencies. -pub fn findRepositories(allocator: Allocator, gir_path: []const std.fs.Dir, roots: []const Include) FindError![]Repository { +pub fn findRepositories(allocator: Allocator, gir_path: []const std.fs.Dir, roots: []const Include, diag: *Diagnostics) Allocator.Error![]Repository { var repos = std.ArrayHashMap(Include, Repository, Include.ArrayContext, true).init(allocator); defer repos.deinit(); errdefer for (repos.values()) |*repo| repo.deinit(); @@ -22,7 +32,10 @@ pub fn findRepositories(allocator: Allocator, gir_path: []const std.fs.Dir, root try needed_repos.appendSlice(roots); while (needed_repos.popOrNull()) |needed_repo| { if (!repos.contains(needed_repo)) { - const repo = try findRepository(allocator, gir_path, needed_repo); + const repo = findRepository(allocator, gir_path, needed_repo, diag) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.FindFailed => continue, + }; try repos.put(needed_repo, repo); try needed_repos.appendSlice(repo.includes); } @@ -31,19 +44,35 @@ pub fn findRepositories(allocator: Allocator, gir_path: []const std.fs.Dir, root return try allocator.dupe(Repository, repos.values()); } -fn findRepository(allocator: Allocator, gir_path: []const std.fs.Dir, include: Include) !Repository { +fn findRepository(allocator: Allocator, gir_path: []const std.fs.Dir, include: Include, diag: *Diagnostics) !Repository { const repo_path = try std.fmt.allocPrintZ(allocator, "{s}-{s}.gir", .{ include.name, include.version }); defer allocator.free(repo_path); + for (gir_path) |dir| { const file = dir.openFile(repo_path, .{}) catch |err| switch (err) { error.FileNotFound => continue, - else => |other| return other, + else => { + try diag.add("failed to open GIR file: {s}: {}", .{ repo_path, err }); + return error.FindFailed; + }, }; defer file.close(); var reader = std.io.bufferedReader(file.reader()); - return try Repository.parse(allocator, reader.reader()); + return Repository.parse(allocator, reader.reader()) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.InvalidGir => { + try diag.add("failed to parse GIR file: {s}", .{repo_path}); + return error.FindFailed; + }, + else => { + try diag.add("failed to read GIR file: {s}: {}", .{ repo_path, err }); + return error.FindFailed; + }, + }; } - return error.RepositoryNotFound; + + try diag.add("failed to find GIR file: {s}", .{repo_path}); + return error.FindFailed; } const ns = struct { diff --git a/src/main.zig b/src/main.zig index f379b19..4923f19 100644 --- a/src/main.zig +++ b/src/main.zig @@ -122,7 +122,18 @@ pub fn main() Allocator.Error!void { var src_out_dir = output_dir.?.makeOpenPath("src", .{}) catch |err| fatal("failed to create output src directory: {}", .{err}); defer src_out_dir.close(); - const repositories = gir.findRepositories(allocator, gir_path.items, roots.items) catch |err| fatal("failed to discover GIR repositories: {}", .{err}); + const repositories = repositories: { + var diag: gir.Diagnostics = .{ .allocator = allocator }; + defer diag.deinit(); + const repositories = try gir.findRepositories(allocator, gir_path.items, roots.items, &diag); + if (diag.errors.items.len > 0) { + for (diag.errors.items) |err| { + log.err("{s}", .{err}); + } + fatal("failed to find and parse GIR repositories", .{}); + } + break :repositories repositories; + }; defer allocator.free(repositories); defer for (repositories) |*repository| repository.deinit(); translate.createBindings(allocator, repositories, bindings_path.items, extensions_path.items, src_out_dir) catch |err| fatal("failed to create bindings: {}", .{err});