diff options
Diffstat (limited to 'build.zig')
| -rw-r--r-- | build.zig | 59 |
1 files changed, 58 insertions, 1 deletions
@@ -93,7 +93,8 @@ pub fn build(b: *std.Build) void { else null; - // build-time IO, only used to slurp the grammars' highlights.scm queries + // build-time IO: slurps the grammars' highlights.scm queries, and reads + // vendor/themes to find the theme sources var threaded: std.Io.Threaded = .init(b.allocator, .{}); defer threaded.deinit(); const io = threaded.io(); @@ -115,6 +116,40 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); + // THEMES. tools/gen_themes.zig turns each vendored helix .toml / zed .json + // into one .zig file per theme plus a list.zig that imports them all; + // src/pardes.zig folds that list into the ring beside the three it ships. + // + // Straight into the build cache and handed over as a module, rather than + // written back into src/: the output then has no freshness problem to own + // (zig re-runs the generator only when a vendored file changes, and a + // cached run leaves the compile's inputs byte-identical, so `zig build` + // with nothing touched still does nothing), there is nothing to gitignore + // and no stale .zig can survive a deleted source. + // + // The INPUT list is read from the directory here rather than written out, + // so adding a theme is dropping a file in — and each file goes in as a + // content-hashed argument, which is what makes that new file re-run the + // step and nothing else. + const theme_gen = b.addExecutable(.{ + .name = "pardes-gen-themes", + .root_module = b.createModule(.{ + .target = b.graph.host, + .optimize = .ReleaseSafe, + .root_source_file = b.path("tools/gen_themes.zig"), + }), + }); + const run_theme_gen = b.addRunArtifact(theme_gen); + const themes_out = run_theme_gen.addOutputDirectoryArg("themes"); + for (vendoredThemes(b, io)) |name| run_theme_gen.addFileArg(b.path(b.fmt("vendor/themes/{s}", .{name}))); + const themes_mod = b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = themes_out.path(b, "list.zig"), + }); + root_mod.addImport("generated_themes", themes_mod); + hx_core_mod.addImport("generated_themes", themes_mod); + // static archives the emcc link needs beyond the zig object (grammars, SDL) var wasm_libs: std.ArrayList(std.Build.LazyPath) = .empty; @@ -713,6 +748,28 @@ fn compileGlsl(b: *std.Build, src: []const u8, stage: []const u8, out: []const u return cmd.addOutputFileArg(out); } +/// The theme sources, sorted. SORTED because the run step is cached by its +/// argv: readdir order is whatever the filesystem feels like, and an argv that +/// shuffles is a cache miss and a rebuild every time. Anything that is not a +/// .toml or a .json is skipped, which is what lets the upstream LICENSE files +/// sit beside the themes they cover. +fn vendoredThemes(b: *std.Build, io: std.Io) []const []const u8 { + var dir = b.build_root.handle.openDir(io, "vendor/themes", .{ .iterate = true }) catch @panic("open vendor/themes"); + defer dir.close(io); + var names: std.ArrayList([]const u8) = .empty; + var it = dir.iterate(); + while (it.next(io) catch @panic("read vendor/themes")) |e| { + if (!std.mem.endsWith(u8, e.name, ".toml") and !std.mem.endsWith(u8, e.name, ".json")) continue; + names.append(b.allocator, b.dupe(e.name)) catch @panic("OOM"); + } + std.mem.sort([]const u8, names.items, {}, struct { + fn lt(_: void, a: []const u8, c: []const u8) bool { + return std.mem.order(u8, a, c) == .lt; + } + }.lt); + return names.items; +} + fn detectEmscriptenSysroot(b: *std.Build) ?[]const u8 { if (b.option([]const u8, "emscripten-sysroot", "path to the Emscripten sysroot")) |path| return path; if (b.sysroot) |path| return path; |
