diff options
Diffstat (limited to 'build.zig')
| -rw-r--r-- | build.zig | 80 |
1 files changed, 73 insertions, 7 deletions
@@ -3,6 +3,9 @@ const builtin = @import("builtin"); const mupdf_build = @import("mupdf.zig"); const snap_build = @import("build/snap.zig"); const grammar_manifest = @import("src/grammar_manifest.zig"); +/// The package manifest, imported UNTYPED on purpose — see the `version` +/// option in `shellOptions` for why that word is load-bearing. +const zon = @import("build.zig.zon"); /// `esp32p4` is not a shell in this package at all: it is one freestanding /// OBJECT, compiled for riscv32-freestanding, which the `zig_p4` firmware @@ -17,11 +20,30 @@ pub const Platform = enum { tty, gui, web, macos, esp32p4 }; /// from this, so there is one string and no drift. pub const macos_min_version: std.SemanticVersion = .{ .major = 13, .minor = 0, .patch = 0 }; -/// The ZLS the language backend links, spelled once. It names the commit -/// build.zig.zon pins (0.16.x branch) and is passed BOTH to ZLS's own -/// `-Dversion-string` (its build.zig otherwise shells out to `git describe`, -/// which fails on a fetched package that has no .git) and to `SPC l i`. +/// The ZLS the language backend links. It names the commit build.zig.zon pins +/// (0.16.x branch) and is passed BOTH to ZLS's own `-Dversion-string` (its +/// build.zig otherwise shells out to `git describe`, which fails on a fetched +/// package that has no .git) and to `SPC l i`. +/// +/// It is spelled TWICE — here and in `.dependencies.zls.url` — and the comment +/// that used to claim "spelled once" was wrong. The duplication is unavoidable +/// (the semver half exists nowhere in the manifest), so the `comptime` block +/// below makes the two AGREE by construction: a .zon bump that forgets this +/// line is now a build error instead of a `SPC l i` that names a build nobody +/// linked. const zls_version = "0.16.1-dev+3e0d0820"; + +comptime { + const plus = std.mem.indexOfScalar(u8, zls_version, '+') orelse + @compileError("zls_version must end in +<short commit>"); + const short = zls_version[plus + 1 ..]; + const url = zon.dependencies.zls.url; + const hash = std.mem.indexOfScalar(u8, url, '#') orelse + @compileError("the zls dependency url must pin a commit with #<sha>"); + if (!std.mem.startsWith(u8, url[hash + 1 ..], short)) @compileError( + "zls_version pins commit " ++ short ++ ", build.zig.zon pins " ++ url[hash + 1 ..], + ); +} pub const TreeSitterGrammars = enum { disabled, zig, minimal, full }; /// The GUI shell's shaders, spelled ONCE. The runtime SPIR-V imports, their @@ -481,6 +503,7 @@ pub fn build(b: *std.Build) void { .theme_animation = theme_animation, .prebuilt_shaders = prebuilt_shaders, .zig_lib_dir = b.graph.zig_lib_directory.path orelse "", + .commit = gitCommit(b), }; // ONE `pardes_config` per distinct frontend in this build: two when the // cli and the gui are made together, one otherwise. hxdiff's core and @@ -1810,6 +1833,39 @@ fn prefixIsUntouched(b: *std.Build) bool { std.mem.eql(u8, b.h_dir, b.pathJoin(&.{ b.install_path, "include" })); } +/// The commit this build came from, or null when there is nothing to say. +/// +/// Read at CONFIGURE time and handed to `pardes_config`, so the binary carries +/// a string rather than the ability to shell out. That is the whole point: a +/// `--version` that runs `git` itself reports the tree it happens to be +/// standing in rather than the one it was built from, and on the board there is +/// no `git` to run and no process to run it with. +/// +/// ABSENCE IS NOT AN ERROR, and must not be. A release tarball has no `.git`, a +/// container may have no `git` binary, and a source drop is not a repository — +/// none of those is a reason to refuse to build. Every way of having no answer +/// (no binary, no repository, a git that exits non-zero, a git that prints +/// nothing) lands on the same `null`, and the frontends print the version alone. +/// +/// Deliberately NOT `--dirty`. Marking a dirty tree costs a worktree stat on +/// every configure, and — the real cost — it would change `pardes_config` on +/// every file edit. Every module in this build imports that options module, so +/// a dirty marker means editing one line rebuilds the world. The commit alone +/// changes only when a commit does. +fn gitCommit(b: *std.Build) ?[]const u8 { + var code: u8 = 0; + // `-C` the build root rather than trusting the cwd: `zig build` may be run + // from anywhere, and a `git` resolved against the wrong directory would + // cheerfully answer about a DIFFERENT repository. + const out = b.runAllowFail( + &.{ "git", "-C", b.build_root.path orelse ".", "rev-parse", "--short=12", "HEAD" }, + &code, + .ignore, + ) catch return null; + const trimmed = std.mem.trim(u8, out, " \t\r\n"); + return if (trimmed.len == 0) null else trimmed; +} + /// Everything `pardes_config` says that does NOT depend on which frontend is /// being built. Gathered into one value so the two options modules a default /// build makes cannot drift apart in any field but the one that is supposed to @@ -1827,6 +1883,10 @@ const ShellConfig = struct { theme_animation: ?bool, prebuilt_shaders: bool, zig_lib_dir: []const u8, + /// Which commit this build came from, or null when there is no answer. Read + /// once in `build()` so the two shells of a default build cannot disagree, + /// and so `git` is spawned once rather than per frontend. + commit: ?[]const u8, }; /// One `pardes_config` options module, for one frontend. `module_names` and @@ -1852,9 +1912,15 @@ fn shellOptions(b: *std.Build, cfg: ShellConfig, platform: Platform) *std.Build. // names: the .zon pins a commit, and a status screen that cannot say WHICH // analyser answered is not worth opening. o.addOption([]const u8, "zls_version", if (cfg.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. - o.addOption([]const u8, "version", "0.0.1"); + // THE version, read from the manifest rather than copied beside it. The + // `@import` being UNTYPED is what makes that possible: annotating its type + // would demand an exact field match and reject `.dependencies`, `.paths` + // and the rest, which is the failure the hand-synced literal that used to + // sit here was working around. Verified against this exact manifest. + o.addOption([]const u8, "version", zon.version); + // ...and the commit it was built from, when there is one. See `gitCommit` + // for why this is optional and why it is read at configure time. + o.addOption(?[]const u8, "commit", cfg.commit); // 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 |
