diff options
Diffstat (limited to 'tools/embed_zig_sources.zig')
| -rw-r--r-- | tools/embed_zig_sources.zig | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/embed_zig_sources.zig b/tools/embed_zig_sources.zig new file mode 100644 index 00000000..a832798c --- /dev/null +++ b/tools/embed_zig_sources.zig @@ -0,0 +1,128 @@ +//! Build-time generator for the web LOOK filesystem. +//! +//! Prefer `git ls-files` so ignored build/cache/vendor files never leak into +//! the browser bundle. A source archive should still be buildable without Git, +//! so a conservative directory walk is the fallback. +const std = @import("std"); + +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; + const args = try init.minimal.args.toSlice(init.arena.allocator()); + if (args.len != 3) return error.BadArgs; + + const root = args[1]; + const output_path = args[2]; + var paths: std.ArrayList([]const u8) = .empty; + defer { + for (paths.items) |path| gpa.free(path); + paths.deinit(gpa); + } + + if (!try gitTrackedZigFiles(gpa, io, root, &paths)) + try walkedZigFiles(gpa, io, root, &paths); + std.mem.sort([]const u8, paths.items, {}, lessThanPath); + + var generated: std.Io.Writer.Allocating = .init(gpa); + defer generated.deinit(); + try generated.writer.writeAll( + \\// Generated by tools/embed_zig_sources.zig. Do not edit. + \\pub const Source = struct { path: []const u8, contents: []const u8 }; + \\pub const all = [_]Source{ + \\ + ); + + for (paths.items) |path| { + const full_path = try std.Io.Dir.path.join(gpa, &.{ root, path }); + defer gpa.free(full_path); + const contents = try std.Io.Dir.cwd().readFileAlloc(io, full_path, gpa, .limited(64 * 1024 * 1024)); + defer gpa.free(contents); + try generated.writer.print(" .{{ .path = \"{f}\", .contents = \"{f}\" }},\n", .{ + std.zig.fmtString(path), + std.zig.fmtString(contents), + }); + } + try generated.writer.writeAll("};\n"); + + const output = if (std.Io.Dir.path.isAbsolute(output_path)) + try std.Io.Dir.createFileAbsolute(io, output_path, .{}) + else + try std.Io.Dir.cwd().createFile(io, output_path, .{}); + defer output.close(io); + try output.writeStreamingAll(io, generated.written()); +} + +/// Returns true only when Git ran successfully. The working-tree contents are +/// read later; Git supplies the tracked path filter, not historical blobs. +fn gitTrackedZigFiles( + gpa: std.mem.Allocator, + io: std.Io, + root: []const u8, + paths: *std.ArrayList([]const u8), +) !bool { + const result = std.process.run(gpa, io, .{ + .argv = &.{ "git", "-C", root, "ls-files", "-z", "--", "*.zig" }, + .stdout_limit = .limited(16 * 1024 * 1024), + .stderr_limit = .limited(1024 * 1024), + }) catch return false; + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); + switch (result.term) { + .exited => |code| if (code != 0) return false, + else => return false, + } + + var it = std.mem.splitScalar(u8, result.stdout, 0); + while (it.next()) |raw_path| { + if (raw_path.len == 0 or !std.mem.endsWith(u8, raw_path, ".zig")) continue; + try paths.append(gpa, try normalizedPath(gpa, raw_path)); + } + return true; +} + +fn walkedZigFiles( + gpa: std.mem.Allocator, + io: std.Io, + root: []const u8, + paths: *std.ArrayList([]const u8), +) !void { + var dir = if (std.Io.Dir.path.isAbsolute(root)) + try std.Io.Dir.openDirAbsolute(io, root, .{ .iterate = true }) + else + try std.Io.Dir.cwd().openDir(io, root, .{ .iterate = true }); + defer dir.close(io); + var walker = try dir.walk(gpa); + defer walker.deinit(); + + while (try walker.next(io)) |entry| { + if (entry.kind == .directory and ignoredFallbackDir(entry.path)) { + walker.leave(io); + continue; + } + if ((entry.kind != .file and entry.kind != .sym_link) or !std.mem.endsWith(u8, entry.path, ".zig")) continue; + try paths.append(gpa, try normalizedPath(gpa, entry.path)); + } +} + +fn ignoredFallbackDir(path: []const u8) bool { + var parts = std.mem.tokenizeAny(u8, path, "/\\"); + while (parts.next()) |part| { + if (std.mem.eql(u8, part, ".git") or + std.mem.eql(u8, part, ".zig-cache") or + std.mem.eql(u8, part, "zig-cache") or + std.mem.eql(u8, part, "zig-out") or + std.mem.eql(u8, part, "zig-pkg")) return true; + } + return false; +} + +fn normalizedPath(gpa: std.mem.Allocator, path: []const u8) ![]u8 { + const copy = try gpa.dupe(u8, path); + if (std.Io.Dir.path.sep != '/') + std.mem.replaceScalar(u8, copy, std.Io.Dir.path.sep, '/'); + return copy; +} + +fn lessThanPath(_: void, a: []const u8, b: []const u8) bool { + return std.mem.order(u8, a, b) == .lt; +} |
