feat: public API and build helper for consumers

- root.zig: re-exports Benchmark, Suite, Options, Format, Result,
  BenchFn, parse_duration_ns; also pulls every submodule into the test
  block so `zig build test` covers them.
- build_helper.zig: add_bench_step(b, opts) factory that creates the
  benchmark executable (ReleaseFast by default), wires zbench and any
  extra modules, attaches an addRunArtifact forwarding `--` args, and
  registers a `zig build <step_name>` step.
This commit is contained in:
2026-05-21 08:13:18 +03:00
parent 4f5f693547
commit a763182b9d
2 changed files with 104 additions and 0 deletions

60
src/build_helper.zig Normal file
View File

@@ -0,0 +1,60 @@
//! Build-system helper. Importable from a user's `build.zig` as
//! `@import("zbench_build")` (alongside `b.dependency("zbench", ...)`).
//!
//! Provides `add_bench_step`, a small factory that wires up a `zig build
//! bench` step around a user-written benchmark executable.
const std = @import("std");
pub const BenchStepOptions = struct {
/// Build step name (e.g. "bench"). The `zig build <name>` invocation
/// triggers the run.
step_name: []const u8 = "bench",
/// Executable name. Defaults to `step_name`.
exe_name: ?[]const u8 = null,
/// Source file containing the user's `pub fn main(init)`.
root: std.Build.LazyPath,
target: std.Build.ResolvedTarget,
/// Optimize mode for the benchmark exe. ReleaseFast by default — there is
/// rarely a reason to benchmark debug builds.
optimize: std.builtin.OptimizeMode = .ReleaseFast,
/// The `zbench` module from `b.dependency("zbench", ...).module("zbench")`.
zbench: *std.Build.Module,
/// Extra modules to import into the benchmark exe (e.g. the code under
/// test).
extra_imports: []const ExtraImport = &.{},
/// Step description shown by `zig build --help`.
description: []const u8 = "Run benchmarks",
};
pub const ExtraImport = struct {
name: []const u8,
module: *std.Build.Module,
};
/// Wire up a `zig build <step_name>` step that compiles the benchmark exe and
/// runs it, forwarding any CLI args after `--` to the benchmark.
///
/// Returns the created `*Step.Compile` so the caller can attach further
/// configuration (link options, install rules, etc.) if needed.
pub fn add_bench_step(b: *std.Build, opts: BenchStepOptions) *std.Build.Step.Compile {
const exe = b.addExecutable(.{
.name = opts.exe_name orelse opts.step_name,
.root_module = b.createModule(.{
.root_source_file = opts.root,
.target = opts.target,
.optimize = opts.optimize,
}),
});
exe.root_module.addImport("zbench", opts.zbench);
for (opts.extra_imports) |imp| {
exe.root_module.addImport(imp.name, imp.module);
}
const run = b.addRunArtifact(exe);
if (b.args) |args| run.addArgs(args);
const step = b.step(opts.step_name, opts.description);
step.dependOn(&run.step);
return exe;
}

44
src/root.zig Normal file
View File

@@ -0,0 +1,44 @@
//! zbench — Go-style benchmarking for Zig 0.16+.
//!
//! ```zig
//! const std = @import("std");
//! const zbench = @import("zbench");
//!
//! pub fn main(init: std.process.Init) !void {
//! var suite = zbench.Suite.init(init.gpa, init.io);
//! defer suite.deinit();
//!
//! try suite.add("append", bench_append);
//! try suite.run_cli(init);
//! }
//!
//! fn bench_append(b: *zbench.Benchmark) !void {
//! var list: std.ArrayListUnmanaged(u8) = .empty;
//! defer list.deinit(b.allocator);
//! b.reset_timer();
//! var i: u64 = 0;
//! while (i < b.n) : (i += 1) {
//! try list.append(b.allocator, @intCast(i & 0xff));
//! }
//! b.keep(list.items);
//! }
//! ```
const bench = @import("benchmark.zig");
const suite_mod = @import("suite.zig");
const runner = @import("runner.zig");
pub const Benchmark = bench.Benchmark;
pub const BenchFn = bench.BenchFn;
pub const Suite = suite_mod.Suite;
pub const Options = suite_mod.Options;
pub const Format = suite_mod.Format;
pub const Result = runner.Result;
pub const parse_duration_ns = suite_mod.parse_duration_ns;
test {
_ = @import("alloc.zig");
_ = @import("stats.zig");
_ = @import("runner.zig");
_ = @import("suite.zig");
}