diff --git a/src/build/GhosttyI18n.zig b/src/build/GhosttyI18n.zig index 7667d30c3..778cfabc5 100644 --- a/src/build/GhosttyI18n.zig +++ b/src/build/GhosttyI18n.zig @@ -79,24 +79,38 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step { xgettext.has_side_effects = true; inline for (gresource.blueprint_files) |blp| { - // We avoid using addFileArg here since the full, absolute file path - // would be added to the file as its location, which differs for - // everyone's checkout of the repository. - // This comes at a cost of losing per-file caching, of course. - xgettext.addArg(std.fmt.comptimePrint( + const path = std.fmt.comptimePrint( "src/apprt/gtk/ui/{[major]}.{[minor]}/{[name]s}.blp", blp, - )); + ); + // The arguments to xgettext must be the relative path in the build root + // or the resulting files will contain the absolute path. This will cause + // a lot of churn because not everyone has the Ghostty code checked out in + // exactly the same location. + xgettext.addArg(path); + // Mark the file as an input so that the Zig build system caching will work. + xgettext.addFileInput(b.path(path)); } { - var gtk_files = try b.build_root.handle.openDir( + // Iterate over all of the files underneath `src/apprt/gtk`. We store + // them in an array so that they can be sorted into a determininistic + // order. That will minimize code churn as directory walking is not + // guaranteed to happen in any particular order. + + var gtk_files: std.ArrayListUnmanaged([]const u8) = .empty; + defer { + for (gtk_files.items) |item| b.allocator.free(item); + gtk_files.deinit(b.allocator); + } + + var gtk_dir = try b.build_root.handle.openDir( "src/apprt/gtk", .{ .iterate = true }, ); - defer gtk_files.close(); + defer gtk_dir.close(); - var walk = try gtk_files.walk(b.allocator); + var walk = try gtk_dir.walk(b.allocator); defer walk.deinit(); while (try walk.next()) |src| { switch (src.kind) { @@ -109,7 +123,29 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step { else => continue, } - xgettext.addArg((b.pathJoin(&.{ "src/apprt/gtk", src.path }))); + try gtk_files.append(b.allocator, try b.allocator.dupe(u8, src.path)); + } + + std.mem.sort( + []const u8, + gtk_files.items, + {}, + struct { + fn lt(_: void, lhs: []const u8, rhs: []const u8) bool { + return std.mem.order(u8, lhs, rhs) == .lt; + } + }.lt, + ); + + for (gtk_files.items) |item| { + const path = b.pathJoin(&.{ "src/apprt/gtk", item }); + // The arguments to xgettext must be the relative path in the build root + // or the resulting files will contain the absolute path. This will + // cause a lot of churn because not everyone has the Ghostty code + // checked out in exactly the same location. + xgettext.addArg(path); + // Mark the file as an input so that the Zig build system caching will work. + xgettext.addFileInput(b.path(path)); } }