Embed git commit in Zig programs
tags: zig
Table of Contents
Whether you are writing a fancy database, or simple CLI program, it's always helpful to embed git commit into the binary.
With it, you can know which exact lines of code is to be blamed when users throw you an unhappy backtrace.
Previously I have done similar thing in Go, and it's fairly easy:
go build -ldflags="-X main.Commit=$(git rev-parse HEAD)"
And in you main.go
, you have something like
var Commit string
How to do this in Zig? The answer is Step/Options.zig
.
In case you don't know how Zig build works, read Zig Build System first.
Options
is a builtin build step to generate module at compile time. The main API to define variable
is addOption
, and after define variables you want to embed, use createModule
to create a module for project to use. Take one example:
// build.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
var opt = b.addOptions();
opt.addOption([]const u8, "git_commit", "HEAD");
const exe = b.addExecutable(.{
.name = "hello",
.root_source_file = .{ .path = "src/main.zig" },
.target = .{},
.optimize = .Debug,
});
exe.addModule("build_info", opt.createModule());
b.installArtifact(exe);
}
// src/main.zig
const std = @import("std");
const build_info = @import("build_info");
pub fn main() !void {
std.debug.print("Commit: {s}.", .{build_info.git_commit});
}
Then build and run, you will get
Commit: HEAD.
Under the hood, Options
step will create a Zig source file in zig-cache
directory, which contains variables you defined via addOption
.
$ rg git_commit
src/main.zig
5: std.debug.print("Commit: {s}.", .{build_info.git_commit});
build.zig
5: opt.addOption([]const u8, "git_commit", "HEAD");
zig-cache/c/165762754dd5d4be37683bba59869ce3/options.zig
1:pub const git_commit: []const u8 = "HEAD";
After understand how Options
step works, the last task to complete our target is to replace hard-coded HEAD
with real commit id, we have two options here:
- Shell out to
git
viaexec
family API, and parse its stdout - Like how
-Dtarget
works, add a new option to pass git commit intobuild.zig
Since solution 2 is both easy and OS-independent, I will only introduce how to do it:
var opt = b.addOptions();
- opt.addOption([]const u8, "git_commit", "HEAD");
+ opt.addOption(
+ []const u8,
+ "git_commit",
+ b.option([]const u8, "git_commit", "Current git commit") orelse "Unknown",
+ );
Then build and run again:
$ zig build -Dgit_commit=$(git rev-parse HEAD)
$ ./zig-out/bin/hello
Commit: 6461dee5fd4337ceb79b3f5e5644685d5324533c.
That's it, git commit is embed in our programs!
PS: When build in GitHub Action, we can use builtin env vars directly:
zig build -Dgit_commit=${{ github.sha }}
.