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-671 — pub 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
Part of #839 — [Audit][Round 2] A++ umbrella.
Phase: P4 · Finding: R2-12
Problem
The top-level
pub fn buildinbuild.zigis 668 lines — the single largest function in the codebase. It bundles: option declarations (~80 lines), module creation, dependency wiring, shader validation (~40validate_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-671—pub fn build(b: *std.Build) voidspans 668 lines.Some decomposition already exists:
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:LODRendererat 552 lines (a struct factory, inherent to Vulkan-style init). The third isvulkan/bloom_system.zig:initat 347 lines.build.zig:buildexceeds both.Fix
Extract focused helpers, each verifiably correct in isolation:
Helper 1 —
defineBuildOptions(b) BuildOptionsMove the ~80 lines of
b.option(...)declarations into one helper that returns a struct of all resolved options. Eliminates ~80 lines frombuildand gives options a single named handle.Helper 2 —
defineModules(b, options) ModulesMove the ~30
b.addModule(...)calls into one helper. Returns a struct of all module references.Helper 3 —
defineShaderValidation(b, modules, options) []StepMove the ~40
validate_vulkan_*steps (currently at the tail ofbuild) into one helper. Returns the validation steps so they can be wired intotestdependency.Helper 4 —
defineInstallSteps(b, ...) voidMove install artifacts, prefixes, and post-install hooks into one helper.
Helper 5 — collapse
addProjectModuleImports's 22 paramsThe 22-parameter helper is itself a smell. Replace with:
Single struct param, named access at call sites.
Target
pub fn buildshrinks 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)-Dskip-present,-Dsmoke-test,-Ddebug_shadows,-Dbenchmark-preset=low,-Dauto-world=flatetc. — confirm they still take effect.Constraints
refactor(build): decompose pub fn build into focused helpers.ProjectModulesstruct change is the riskiest (touches all 22 callers) — consider landing as a separate PR.Notes
build.zigitself by area (build_options.zig,build_modules.zig, etc.) — out of scope here.Tracking: #839