Skip to content
zit Artifact [ae2afaa5db]
Login

Artifact [ae2afaa5db]

Artifact ae2afaa5db69e457e2370822b5ef33b93be233b9173540bb646ec3b4864fb9b7:


const std = @import("std");

/// Config threaded into each per-artifact builder: target/optimize choice
/// plus the `zit` library module every executable imports.
const Env = struct {
    b: *std.Build,
    target: std.Build.ResolvedTarget,
    optimize: std.builtin.OptimizeMode,
    mod: *std.Build.Module,
};

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // The zit library: a Zig implementation of the Fossil formats and protocol
    // as specified by the four RFC drafts in docs/rfcs/.
    const mod = b.addModule("zit", .{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
    });
    const env = Env{ .b = b, .target = target, .optimize = optimize, .mod = mod };

    // `zig build test` — runs library + CLI unit tests; each helper below
    // wires its own artifact's inline tests into this step as it goes.
    const test_step = b.step("test", "Run unit tests");
    const mod_tests = b.addTest(.{ .root_module = mod });
    test_step.dependOn(&b.addRunArtifact(mod_tests).step);

    build_cli(env, test_step);
    build_fuzz(env);
    build_dst(env, test_step);
    build_bench(b);
    build_smoke(env, test_step);
}

/// The `zit` command-line tool: `zig build` installs it, `zig build run --
/// <args>` runs it, and its own unit tests join `test_step`.
fn build_cli(env: Env, test_step: *std.Build.Step) void {
    const exe = env.b.addExecutable(.{
        .name = "zit",
        .root_module = env.b.createModule(.{
            .root_source_file = env.b.path("src/main.zig"),
            .target = env.target,
            .optimize = env.optimize,
            .imports = &.{
                .{ .name = "zit", .module = env.mod },
            },
        }),
    });
    env.b.installArtifact(exe);

    // `zig build run -- <args>`
    const run_step = env.b.step("run", "Run the zit CLI");
    const run_cmd = env.b.addRunArtifact(exe);
    run_step.dependOn(&run_cmd.step);
    run_cmd.step.dependOn(env.b.getInstallStep());
    if (env.b.args) |args| run_cmd.addArgs(args);

    const exe_tests = env.b.addTest(.{ .root_module = exe.root_module });
    test_step.dependOn(&env.b.addRunArtifact(exe_tests).step);
}

/// `zig build fuzz -- <target> [--seed N] [--rounds N]` — seeded deterministic
/// fuzz targets, driven in a loop by an out-of-band runner; not part of `test`.
fn build_fuzz(env: Env) void {
    const fuzz_exe = env.b.addExecutable(.{
        .name = "fuzz",
        .root_module = env.b.createModule(.{
            .root_source_file = env.b.path("src/fuzz_tests.zig"),
            .target = env.target,
            .optimize = env.optimize,
            .imports = &.{.{ .name = "zit", .module = env.mod }},
        }),
    });
    const fuzz_step = env.b.step("fuzz", "Run a seeded fuzz target (zig build fuzz -- <target> --seed N)");
    const fuzz_run = env.b.addRunArtifact(fuzz_exe);
    if (env.b.args) |args| fuzz_run.addArgs(args);
    fuzz_step.dependOn(&fuzz_run.step);
}

/// `zig build dst [-- --seed N | --seeds K] [--steps M]` — deterministic
/// simulation of the repostate derivation. No args runs the fixed smoke
/// sweep (seeds 1..25 x 10_000 steps); deep sweeps pass `--seeds`.
///
/// dst.zig's module also reaches sim/*.zig and storage.zig (both kept
/// out of the public `zit` module); running it as a test binary is how
/// their inline unit tests join `zig build test`.
fn build_dst(env: Env, test_step: *std.Build.Step) void {
    const dst_exe = env.b.addExecutable(.{
        .name = "dst",
        .root_module = env.b.createModule(.{
            .root_source_file = env.b.path("src/dst.zig"),
            .target = env.target,
            .optimize = env.optimize,
            .imports = &.{.{ .name = "zit", .module = env.mod }},
        }),
    });
    const dst_step = env.b.step("dst", "Deterministic simulation (default smoke sweep; zig build dst -- --seed N)");
    const dst_run = env.b.addRunArtifact(dst_exe);
    if (env.b.args) |args| dst_run.addArgs(args);
    dst_step.dependOn(&dst_run.step);

    const dst_tests = env.b.addTest(.{ .root_module = dst_exe.root_module });
    test_step.dependOn(&env.b.addRunArtifact(dst_tests).step);
}

/// `bench` is wired at a later milestone (a benchmark suite); until then
/// it exists as a named step that fails loudly rather than silently
/// passing.
fn build_bench(b: *std.Build) void {
    const bench_step = b.step("bench", "Benchmarks");
    bench_step.dependOn(&b.addFail("bench: no benchmark exists yet").step);
}

/// `zig build smoke` — the one sanctioned use of the real `fossil` binary:
/// confirms zit-produced artifacts/deltas against fossil's own
/// parser/hasher/store. Confined to this step; never part of `test` or
/// pre-commit. `zig build test` still compiles smoke.zig (catching type
/// errors) without running its fossil-driving scenarios.
fn build_smoke(env: Env, test_step: *std.Build.Step) void {
    const smoke_exe = env.b.addExecutable(.{
        .name = "smoke",
        .root_module = env.b.createModule(.{
            .root_source_file = env.b.path("src/smoke.zig"),
            .target = env.target,
            .optimize = env.optimize,
            .imports = &.{.{ .name = "zit", .module = env.mod }},
        }),
    });
    test_step.dependOn(&smoke_exe.step);

    const smoke_step = env.b.step("smoke", "Fossil interop smoke check (milestone-manual)");
    const smoke_run = env.b.addRunArtifact(smoke_exe);
    if (env.b.args) |args| smoke_run.addArgs(args);
    smoke_step.dependOn(&smoke_run.step);
}