Skip to content

[R2-12][P4] Decompose build.zig:build (668-line function → focused helpers) #851

Description

@MichaelFisher1997

Part of #839 — [Audit][Round 2] A++ umbrella.
Phase: P4 · Finding: R2-12

Problem

The top-level pub fn build in build.zig is 668 lines — the single largest function in the codebase. It bundles: option declarations (~80 lines), module creation, dependency wiring, shader validation (~40 validate_vulkan_* steps at the tail), and install steps into one linear procedural body. Hard to navigate, hard to extend, hard to review.

Evidence

build.zig:3-671pub fn build(b: *std.Build) void spans 668 lines.

Some decomposition already exists:

fn addCimgui(...) void { ... }
fn addSharedImports(...) void { ... }
fn addSharedImportsNoOptions(...) void { ... }
fn addProjectModuleImports(module, *22 module params) void { ... }  // ← 22-param smell

But the bulk — options, module creation, shader validation — is one linear function.

For scale: the second-largest function in the codebase is lod_renderer.zig:LODRenderer at 552 lines (a struct factory, inherent to Vulkan-style init). The third is vulkan/bloom_system.zig:init at 347 lines. build.zig:build exceeds both.

Fix

Extract focused helpers, each verifiably correct in isolation:

Helper 1 — defineBuildOptions(b) BuildOptions

Move the ~80 lines of b.option(...) declarations into one helper that returns a struct of all resolved options. Eliminates ~80 lines from build and gives options a single named handle.

Helper 2 — defineModules(b, options) Modules

Move the ~30 b.addModule(...) calls into one helper. Returns a struct of all module references.

Helper 3 — defineShaderValidation(b, modules, options) []Step

Move the ~40 validate_vulkan_* steps (currently at the tail of build) into one helper. Returns the validation steps so they can be wired into test dependency.

Helper 4 — defineInstallSteps(b, ...) void

Move install artifacts, prefixes, and post-install hooks into one helper.

Helper 5 — collapse addProjectModuleImports's 22 params

The 22-parameter helper is itself a smell. Replace with:

const ProjectModules = struct {
    engine_core: *std.Build.Module,
    engine_math: *std.Build.Module,
    // ... all 22
};
fn addProjectModuleImports(mod: *std.Build.Module, deps: ProjectModules) void

Single struct param, named access at call sites.

Target

pub fn build shrinks from 668 lines to ~50 lines of orchestration: const opts = defineBuildOptions(b); const mods = defineModules(b, opts); const validation = defineShaderValidation(b, mods, opts); const install = defineInstallSteps(b, mods); b.getInstallStep().dependOn(&validation.step);

Verification

  • nix develop --command zig build (full build must succeed unchanged)
  • nix develop --command zig build test (includes shader validation)
  • nix develop --command zig build -Doptimize=ReleaseFast (release build)
  • nix develop --command zig build run (smoke run)
  • All build options still respected: try -Dskip-present, -Dsmoke-test, -Ddebug_shadows, -Dbenchmark-preset=low, -Dauto-world=flat etc. — confirm they still take effect.
  • Single PR — behavior-preserving mechanical refactor.

Constraints

  • Behavior-preserving. Build output and option semantics unchanged.
  • Conventional commit: refactor(build): decompose pub fn build into focused helpers.
  • Do not change the public build option names — downstream tooling (Nix, CI, AI workflows) depends on them.
  • The ProjectModules struct change is the riskiest (touches all 22 callers) — consider landing as a separate PR.

Notes

  • This is the lowest-urgency architectural issue in Round 2 but it pays dividends every time someone touches the build (new module, new option, new shader).
  • A future stretch: split build.zig itself by area (build_options.zig, build_modules.zig, etc.) — out of scope here.

Tracking: #839

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingbuildenhancementNew feature or requestquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions