diff options
Diffstat (limited to 'build.zig')
| -rw-r--r-- | build.zig | 181 |
1 files changed, 133 insertions, 48 deletions
@@ -154,28 +154,6 @@ pub fn build(b: *std.Build) void { } shaders_step.dependOn(&update_shaders.step); - // Web LOOK has a tiny read-only source filesystem. Generate one Zig module - // on the host from every tracked or new/nonignored .zig path + its current - // working-tree contents. The generator prefers Git as the ignore-aware - // path filter and has a conservative filesystem fallback for source - // archives without `.git`. - const source_gen = b.addExecutable(.{ - .name = "pardes-embed-zig-sources", - .root_module = b.createModule(.{ - .target = b.graph.host, - .optimize = .ReleaseSafe, - .root_source_file = b.path("tools/embed_zig_sources.zig"), - }), - }); - const run_source_gen = b.addRunArtifact(source_gen); - run_source_gen.addDirectoryArg(b.path(".")); - const embedded_sources = run_source_gen.addOutputFileArg("embedded_sources.zig"); - // The tracked set can change without any already-known input changing, so - // discovery intentionally runs whenever a consumer builds it. - run_source_gen.has_side_effects = true; - const inspect_sources = b.addInstallFileWithDir(embedded_sources, .{ .custom = "web" }, "embedded_sources.zig"); - b.step("embed-web-sources", "generate zig-out/web/embedded_sources.zig from working-tree .zig files").dependOn(&inspect_sources.step); - snap_build.addWeb(b); if (is_web and !is_web_target) return failBuild(b, web_step, "-Dplatform=web requires -Dtarget=wasm32-freestanding"); @@ -212,6 +190,40 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); + // `pardes-isolate`: a THIRD compilation of the same graph whose only + // difference is one comptime bool. It cannot be the same binary with a + // flag, because the point is that the isolated build does not CONTAIN the + // filesystem: look.zig's libc paths compile away, so no runtime mistake + // can reach a disk that a `--flag` build still links. + // + // Only the tty platform has one. gui adds a GPU it would still need, and + // web/macos are libraries whose host owns main(). + const isolated_mod: ?*std.Build.Module = if (platform == .tty) b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/main.zig"), + .link_libc = true, + }) else null; + + // Every module that compiles src/pardes.zig, so its wiring is applied once + // per dependency instead of once per module per dependency. + var core_mods_buf: [3]*std.Build.Module = .{ root_mod, hx_core_mod, undefined }; + var core_mods_len: usize = 2; + if (isolated_mod) |m| { + core_mods_buf[2] = m; + core_mods_len = 3; + } + const core_mods = core_mods_buf[0..core_mods_len]; + + // The conditional dependencies, hoisted so one `wireCore` call at the end + // can name them all. The unconditional ones are ordinary consts below. + var zls_mod: ?*std.Build.Module = null; + var ts_mod: ?*std.Build.Module = null; + var ts_queries_opts: ?*std.Build.Step.Options = null; + var mupdf_core_mod: ?*std.Build.Module = null; + var ghostty_core_vt: ?*std.Build.Module = null; + var ts_libs: std.ArrayList(*std.Build.Step.Compile) = .empty; + // 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. @@ -243,8 +255,6 @@ pub fn build(b: *std.Build) void { .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); // forkpty: in libc proper on glibc>=2.34 and darwin; the BSDs keep it in libutil if (target.result.os.tag == .freebsd or target.result.os.tag == .netbsd or target.result.os.tag == .openbsd) @@ -273,6 +283,9 @@ pub fn build(b: *std.Build) void { // names: the .zon pins a commit, and a status screen that cannot say WHICH // analyser answered is not worth opening. opts.addOption([]const u8, "zls_version", if (zls_backend) zls_version else "none"); + // Mirrors build.zig.zon's `.version`; kept in sync by hand because a typed + // @import of the .zon rejects its other fields on this Zig 0.16 build. + opts.addOption([]const u8, "version", "0.0.1"); // The stdlib this binary was compiled against, so `gd` on `std.mem.count` // can open the same mem.zig the compiler used. ZLS resolves `@import("std")` // through `zig_lib_dir` and nothing else; without it every std symbol is a @@ -299,8 +312,7 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .@"version-string" = @as([]const u8, zls_version), }); - root_mod.addImport("zls", zls_dep.module("zls")); - hx_core_mod.addImport("zls", zls_dep.module("zls")); + zls_mod = zls_dep.module("zls"); } // Tracy zones (src/tracy.zig): compile the client into the binary only when @@ -354,8 +366,7 @@ pub fn build(b: *std.Build) void { .flags = &.{ "-std=c11", "-DNDEBUG=1" }, }); } - root_mod.addImport("tree-sitter", tree_sitter_mod); - hx_core_mod.addImport("tree-sitter", tree_sitter_mod); + ts_mod = tree_sitter_mod; const ts_queries = b.addOptions(); inline for (grammar_manifest.all) |g| { @@ -386,12 +397,10 @@ pub fn build(b: *std.Build) void { if (g.scanner) lib.root_module.addCSourceFile(.{ .file = dep.path(g.src ++ "/scanner.c"), .flags = &ts_cflags }); lib.root_module.addIncludePath(dep.path(g.src)); if (is_web) lib.root_module.addIncludePath(b.path("src/web/libc/include")); - root_mod.linkLibrary(lib); - hx_core_mod.linkLibrary(lib); + ts_libs.append(b.allocator, lib) catch @panic("OOM"); } } - root_mod.addOptions("ts_queries", ts_queries); - hx_core_mod.addOptions("ts_queries", ts_queries); + ts_queries_opts = ts_queries; } // zstbi (C stb_image): image pane decode. The freestanding core has no C @@ -403,16 +412,12 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .root_source_file = b.path("src/web/zstbi.zig"), }); - root_mod.addImport("zstbi", zstbi_mod); - hx_core_mod.addImport("zstbi", zstbi_mod); // mvzr: the regex engine behind `s` / `S` (helix select/split on a regex). // A bytecode VM that compiles a RUNTIME pattern into a fixed-size struct // with no allocator at all, which is exactly the shape an interactive // prompt needs — see applySelRegex in src/pardes.zig. const mvzr_mod = b.dependency("mvzr", .{ .target = target, .optimize = optimize }).module("mvzr"); - root_mod.addImport("mvzr", mvzr_mod); - hx_core_mod.addImport("mvzr", mvzr_mod); // MuPDF is the default native PDF engine, but remains a genuinely optional // dependency: with -Dmupdf=false (and by default on web), the lazy source @@ -439,8 +444,7 @@ pub fn build(b: *std.Build) void { .file = b.path("src/pdf_bridge.c"), .flags = &.{"-std=gnu11"}, }); - root_mod.addImport("mupdf", mupdf_mod); - hx_core_mod.addImport("mupdf", mupdf_mod); + mupdf_core_mod = mupdf_mod; // Compile the @cImport + exception bridge and exercise the Zig // wrapper's caller-owned RGBA boundary alongside the C end-to-end // document probe below. @@ -604,15 +608,26 @@ pub fn build(b: *std.Build) void { const ghostty_vt = dep.module("ghostty-vt"); ghostty_vt_for_snap = ghostty_vt; ghostty_vt.addImport("uucode", uucode_mod); - root_mod.addImport("ghostty-vt", ghostty_vt); - hx_core_mod.addImport("ghostty-vt", ghostty_vt); + ghostty_core_vt = ghostty_vt; } // override uucode on vaxis LAST so it wins over ghostty's build() wiring. vaxis_mod.addImport("uucode", uucode_mod); - root_mod.addImport("vaxis", vaxis_mod); - hx_core_mod.addImport("vaxis", vaxis_mod); - root_mod.addImport("uucode", uucode_mod); - hx_core_mod.addImport("uucode", uucode_mod); + + // One call per module instead of one line per module per dependency, and + // BEFORE the fold below, which reads what these imports put in the table. + for (core_mods) |m| wireCore(m, .{ + .themes = themes_mod, + .zls = zls_mod, + .tree_sitter = ts_mod, + .ts_libs = ts_libs.items, + .ts_queries = ts_queries_opts, + .zstbi = zstbi_mod, + .mvzr = mvzr_mod, + .mupdf = mupdf_core_mod, + .ghostty_vt = ghostty_core_vt, + .vaxis = vaxis_mod, + .uucode = uucode_mod, + }); // --- the dependency module map, for `gd` on `@import("vaxis")` --- // @@ -662,10 +677,12 @@ pub fn build(b: *std.Build) void { opts.addOption([]const []const u8, "module_names", mod_names); opts.addOption([]const []const u8, "module_roots", mod_roots); - root_mod.addOptions("pardes_config", opts); - hx_core_mod.addOptions("pardes_config", opts); const gui_effect_source_dir = if (gui_shader_sources_prebuilt) "shaders/prebuilt" else "shaders"; - for ([_]*std.Build.Module{ root_mod, hx_core_mod }) |mod| { + // Attached after the fold above, for the reason `opts` names, and with the + // embedded FILES: those are bytes rather than modules, and adding them + // earlier would list them in the module map as if they were importable. + for (core_mods) |mod| { + mod.addOptions("pardes_config", opts); if (platform == .gui) for (gui_shaders) |name| mod.addAnonymousImport( b.fmt("effect-source-{s}.glsl", .{name}), .{ .root_source_file = b.path(b.fmt("{s}/{s}.glsl", .{ gui_effect_source_dir, name })) }, @@ -673,6 +690,24 @@ pub fn build(b: *std.Build) void { if (platform == .macos) mod.addAnonymousImport("effect-source-crt.ci.metal", .{ .root_source_file = b.path("shaders/crt.ci.metal"), }); + // The virtual filesystem's two repo-root entries: `@embedFile` cannot + // reach outside the module's own directory, so they arrive by name. + // See src/source_manifest.zig. + mod.addAnonymousImport("root-build.zig", .{ .root_source_file = b.path("build.zig") }); + mod.addAnonymousImport("root-build.zig.zon", .{ .root_source_file = b.path("build.zig.zon") }); + } + + // The one setting that produces a SECOND executable rather than changing + // this one: its own tiny options module, because duplicating `opts` to + // flip a single bool would be twenty lines that must then agree forever. + const iso_off = b.addOptions(); + iso_off.addOption(bool, "isolated", false); + root_mod.addOptions("pardes_isolation", iso_off); + hx_core_mod.addOptions("pardes_isolation", iso_off); + if (isolated_mod) |m| { + const iso_on = b.addOptions(); + iso_on.addOption(bool, "isolated", true); + m.addOptions("pardes_isolation", iso_on); } // SDL3 shell wiring (-Dplatform=gui). SDL3 and FreeType are both built @@ -723,7 +758,6 @@ pub fn build(b: *std.Build) void { root_mod.addAnonymousImport("embedded.dump.zon", .{ .root_source_file = if (std.fs.path.isAbsolute(dp)) .{ .cwd_relative = dp } else b.path(dp), }); - root_mod.addAnonymousImport("embedded_sources", .{ .root_source_file = embedded_sources }); } if (platform == .web) { @@ -1009,6 +1043,24 @@ pub fn build(b: *std.Build) void { b.installArtifact(exe); const run = b.addRunArtifact(exe); b.step("run", "descend to the pardes").dependOn(&run.step); + // `pardes-isolate`: the SAME source on the core's own defaults — the + // embedded source filesystem, the in-process clipboard, silent ptys. + // Its host keeps only the terminal it draws on, so nothing is forked + // and there is no filesystem in the binary to reach. A pardes that can + // only read itself. + if (isolated_mod) |iso| { + const iso_exe = b.addExecutable(.{ + .name = "pardes-isolate", + .linkage = if (static) .static else null, + .root_module = iso, + }); + b.installArtifact(iso_exe); + const run_iso = b.addRunArtifact(iso_exe); + // Something to look at on arrival, read out of the binary itself. + if (b.args) |args| run_iso.addArgs(args) else run_iso.addArg("src/pardes.zig"); + b.step("run-isolated", "descend to a pardes with no host but the terminal") + .dependOn(&run_iso.step); + } web_step.dependOn(&b.addFail("web needs -Dplatform=web -Dtarget=wasm32-freestanding -Ddump=<dump.zon>").step); // snapshot parity suite: scripted input traces in a pty, captured grids @@ -1309,3 +1361,36 @@ fn vendoredThemes(b: *std.Build, io: std.Io) []const []const u8 { }.lt); return names; } + +/// Everything every compilation of src/pardes.zig needs, in one call per module +/// rather than a line per module beside every dependency — which is the shape +/// that let a third module quietly miss two of them. +/// +/// Fields are in the order these used to be attached in, because that order IS +/// the module map `Debug` prints (import_table iterates by insertion), and a +/// refactor that renumbers a user-visible list changed something. +fn wireCore(m: *std.Build.Module, d: struct { + themes: ?*std.Build.Module = null, + zls: ?*std.Build.Module = null, + tree_sitter: ?*std.Build.Module = null, + ts_libs: []const *std.Build.Step.Compile = &.{}, + ts_queries: ?*std.Build.Step.Options = null, + zstbi: ?*std.Build.Module = null, + mvzr: ?*std.Build.Module = null, + mupdf: ?*std.Build.Module = null, + ghostty_vt: ?*std.Build.Module = null, + vaxis: ?*std.Build.Module = null, + uucode: ?*std.Build.Module = null, +}) void { + if (d.themes) |x| m.addImport("generated_themes", x); + if (d.zls) |x| m.addImport("zls", x); + if (d.tree_sitter) |x| m.addImport("tree-sitter", x); + for (d.ts_libs) |lib| m.linkLibrary(lib); + if (d.ts_queries) |x| m.addOptions("ts_queries", x); + if (d.zstbi) |x| m.addImport("zstbi", x); + if (d.mvzr) |x| m.addImport("mvzr", x); + if (d.mupdf) |x| m.addImport("mupdf", x); + if (d.ghostty_vt) |x| m.addImport("ghostty-vt", x); + if (d.vaxis) |x| m.addImport("vaxis", x); + if (d.uucode) |x| m.addImport("uucode", x); +} |
