.fingerprint value that Zig 0.16 expects in a package's build.zig.zon, and prints it on standard output.
- Zig 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The README mentioned the `zig_fingerprint` module only in passing, which was
not enough to actually depend on it. Cover adding the dependency, wiring the
module into a build, and the surface of the `Fingerprint` type, along with the
two things that trip people up: `format` needs the `{f}` specifier, and
`generate` takes an `std.Io` because Zig 0.16 moved randomness onto the I/O
interface.
The snippets were checked by building a dependent package against this one.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XkhVPSDDeCM3L9QooPM7Z7
|
||
| LICENSES | ||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| README.md | ||
zig-fingerprint
Computes the .fingerprint value that Zig 0.16 expects in a package's
build.zig.zon, and prints it on standard output.
zig init generates that field for you when a package is created, but there is
no supported way to ask the toolchain for one on its own. This tool fills that
gap: it is useful when you are writing a build.zig.zon by hand, generating one
from a template, or repairing the field after renaming a package.
How a fingerprint is built
A fingerprint is a u64 made of two independent halves:
| Bits | Field | Value |
|---|---|---|
| 0–31 | id |
Drawn at random once, when the package is created, and then never changed. |
| 32–63 | checksum |
CRC-32 (ISO-HDLC, the variant used by gzip and zlib) of the package name. |
Together with the name, the id is what gives a package a globally unique
identity, which is how Zig can tell that two packages are versions of one
another rather than unrelated. The checksum half is derived, so Zig recomputes
it from the .name field and rejects the package when it disagrees. That check
is what stops a fingerprint from being copied verbatim into a differently named
package in an attempt to assume its identity.
Because only the id half is random, a fingerprint survives a rename intact:
keep the id, recompute the checksum, and the package keeps its identity.
Building
$ zig build
$ zig build test
The executable lands in zig-out/bin/zig-fingerprint. zig build run -- <args>
runs it without installing.
Usage
usage: zig-fingerprint [options] <name>
options:
-i, --id <id> Use <id> as the low 32 bits instead of drawing them
randomly. Preserves the identity of an existing
package whose name changed.
-c, --check <fp> Reuse the id of the existing fingerprint <fp> and exit
with status 1 if its checksum does not match <name>.
-h, --help Print this help and exit.
<name> is the .name field written without its leading dot. Numbers may be
written in decimal or with a 0x, 0o or 0b prefix. Only the fingerprint is
written to standard output; diagnostics go to standard error.
Generate a fingerprint for a new package:
$ zig-fingerprint my_package
0x5491cbb93f059bf2
Rename a package without losing its identity, by carrying the old id — the low
eight hex digits of the old fingerprint — over to the new name:
$ zig-fingerprint --id 0x5cc769f2 zig_fingerprint
0x3ee809e95cc769f2
Check that a fingerprint already in a build.zig.zon belongs to the name beside
it. The corrected fingerprint is printed either way, and the exit status is 1
when the two disagree:
$ zig-fingerprint --check 0xdeadbeef5cc769f2 zig_fingerprint
0x3ee809e95cc769f2
fingerprint 0xdeadbeef5cc769f2 does not belong to 'zig_fingerprint'; its checksum should be 0x3ee809e9
Using it as a library
The package exposes a zig_fingerprint module, so the same computation is
available to your own build scripts and tools without shelling out to the
executable.
Add it as a dependency, either from a URL or from a path relative to your build root:
$ zig fetch --save <url>
Then wire the module into whichever module needs it:
const zig_fingerprint = b.dependency("zig_fingerprint", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zig_fingerprint", zig_fingerprint.module("zig_fingerprint"));
And use it:
const std = @import("std");
const Fingerprint = @import("zig_fingerprint").Fingerprint;
pub fn main(init: std.process.Init) !void {
// A brand new identity for a package.
const fresh: Fingerprint = .generate(init.io, "my_package");
// The same identity carried over to a renamed package.
const renamed: Fingerprint = .init("my_other_package", fresh.id);
std.debug.assert(renamed.validate("my_other_package"));
std.debug.print("{f}\n", .{renamed});
}
API
Fingerprint is a packed struct(u64) with two u32 fields, id (the low
half) and checksum (the high half), so it can be bit-cast to and from the
integer that appears in a manifest.
| Declaration | Purpose |
|---|---|
init(name, id) Fingerprint |
The fingerprint for name, reusing an id you already have. |
generate(io, name) Fingerprint |
The fingerprint for name, with a freshly drawn random id. |
fromInt(u64) Fingerprint |
Reinterprets a manifest value, such as one you parsed out of a build.zig.zon. |
int() u64 |
The value to write back into a manifest. |
validate(name) bool |
Whether Zig would accept this fingerprint for a package named name. |
format(writer) |
Renders as 0x followed by 16 hex digits, the way build.zig.zon spells it. |
min_id, max_id |
The inclusive bounds Zig reserves for ids, 0x1 and 0xfffffffe. |
checksumOf(name) u32 |
The checksum half on its own, for comparing against an existing fingerprint. |
Two details are easy to trip over. format is a method rather than a default
rendering, so it needs the {f} specifier — plain {} will not compile.
And generate takes an std.Io because Zig 0.16 moved randomness onto the I/O
interface; std.crypto.random no longer exists. If you have no Io at hand,
draw the id yourself and call init instead.
License
MIT. This project follows the REUSE specification:
every file carries its own SPDX headers and the license text lives in
LICENSES/MIT.txt.