refactor!: rewrite zbuild as comptime library#7
Conversation
Three-phase refactor: single ZON file, std.zon.parse-based Config, static build.zig. See docs/superpowers/specs/ for details. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e of truth - Rename test fixtures from .zbuild.zon to .build.zig.zon - Default zbuild_file to build.zig.zon - Remove syncManifest from cmd_sync (no more manifest generation) - Remove Manifest.zig dependency from cmd_init and cmd_fetch - Delete sync_manifest.zig and Manifest.zig (parallel data model eliminated) - Merge zbuild.zon content into build.zig.zon, delete zbuild.zon - Simplify cmd_fetch to delegate entirely to zig fetch Fixes: 2.9, 2.10, 2.12, 2.13, 4.5, 5.8 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change fingerprint from []const u8 to u64 (matches ZON directly) - Remove all deinit methods (arena handles cleanup) - Replace hand-rolled if/else if parser with: - inline for over struct fields for typed values - fromZoirNode for standard types - Custom parsers only for Dependency, ModuleLink, Option - parseHashMap replaces both parseHashMap and parseOptionalHashMap - Ignore unknown top-level fields (enables single-file approach) - Fix dependency parsing to include hash and lazy fields - Fix parseRun to use parseString (Run = []const u8) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create build_runner.zig with configureBuild() that reads build.zig.zon and configures the build graph via direct std.Build API calls - Replace cmd_sync codegen with static build.zig template that imports zbuild and calls configureBuild - Delete ConfigBuildgen.zig (~1280 lines) and sync_build_file.zig (~38 lines) - Hand-write zbuild's own build.zig (can't self-reference) - Expose configureBuild as public API via main.zig - Update sync test to verify static template generation Eliminates string-concatenation codegen, scratch buffers, and zig fmt post-processing. Fixes bugs 1.6, 2.5, 2.6, 2.14, 3.7, 4.1, 4.3, 4.6, 5.5, 5.6, 5.10. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
16 inline tests covering: minimal config, modules, executables (inline and named module refs), dependencies (with hash/lazy/args), libraries, tests, runs, options, options_modules, fmts, module imports, description/ keywords, and error cases (missing required fields, invalid versions). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug fixes: - Implement serialization of libraries, objects, tests, fmts, runs sections (previously commented out, issue 2.7) - Implement enum and enum_list option serialization (previously TODO stubs, issue 2.8) - Add hash and lazy fields to dependency serialization (issue 2.10) Tests: - 8 round-trip tests that parse → serialize → re-parse and verify structural equivalence for each section type Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The depends_on field was parsed but never used in the build graph. Now build_runner tracks install steps in a map and adds step dependencies in a final pass after all artifacts are created. Also adds parser + round-trip tests for depends_on. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug fixes: - ZigEnv: change 'and' to 'or' in exit code check so non-zero exits and signal terminations are properly detected (issue 1.3) - GlobalOptions: add args.next() when consuming --no-sync flag to prevent infinite loop (issue 1.4) - Config: implement write_files parser (was a stub that silently discarded all write_files entries) (issue 1.1) - Args: fix test calling non-existent Args.parse, should be Args.initFromString (issue 3.9) Tests: - GlobalOptions: --no-sync flag consumption (verifies no infinite loop) - Config: write_files parsing with file and dir items - Config: write_files round-trip serialization Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- main.zig: add defer wip_bundle.deinit() so the error bundle's internal allocations are freed on the success path (issue 3.4) - Config.zig: returnParseErrorFmt now sets .owned = true since the message is heap-allocated via allocPrint (issue 3.10) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
zbuild no longer ships a CLI binary. Users consume it as a standard Zig dependency via build.zig.zon and call zbuild.configureBuild(b) from their build.zig. This eliminates ~1,100 lines of CLI indirection that was just wrapping zig build/fetch/init commands. Deleted: Args, GlobalOptions, ZigEnv, Package, run_zig, cmd_build, cmd_fetch, cmd_init, cmd_sync, test/sync, test/fixtures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete Config.zig entirely (2,112 lines — parser, serializer, IR types, tests). The Zig compiler now parses build.zig.zon via @import, and build_runner.zig walks the comptime anonymous struct directly using inline for + @Hasfield. This resolves the dependency args impedance mismatch: since the manifest is comptime-known, dependency .args flow through to b.dependency() naturally without needing a runtime→comptime bridge. Project shrinks from ~2,630 to ~583 lines of source. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- #1: Clean stale build.zig.zon (remove deleted exe/test refs, bump to 0.3.0) - #2: Remove @ptrCast for dest_sub_path (Zig coerces comptime strings) - #3: Default modules to public (export to b.modules unless private = true) - #4: @CompileError for unknown option types + validateManifest for unknown top-level fields - #5: resolveImport returns error.ModuleNotFound instead of @Panic - #6: Remove duplicate modules.put (createModule handles it, callers don't) - #7: Add 8 comptime helper tests (toStringSlice, toEnumSlice, isIntType, isFloatType, isKnownField, validateManifest) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of rejecting unknown top-level fields (which breaks forward compatibility with new Zig versions), validate semantic cross-references: root_module refs point to declared modules, depends_on refs point to declared artifacts, and imports reference modules/options_modules/deps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dual-form syntax (bare tuple for simple commands, struct with cmd + env/cwd/stdio/stdin/depends_on for complex ones), comptime validation, and cmd: step prefix to avoid collision with executable run: steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three tasks: validation, createRun rewrite, and tests. Covers dual-form syntax, stdin/stdin_file exclusion, depends_on wiring, and cmd: step prefix to avoid executable collision. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cross-reference depends_on against declared artifacts and enforce stdin/stdin_file mutual exclusion at compile time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Short form (bare tuple) for simple commands, long form (struct with cmd + cwd/env/inherit_stdio/stdin/stdin_file/depends_on) for complex ones. Replaces runtime string splitting with comptime toStringSlice. Step prefix changes from run: to cmd: to avoid executable collision. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers short-form tuples, long-form structs with depends_on/env/cwd, run+executable name coexistence, and forward-compat unknown fields. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge three separate artifact-section loops in validateManifest into a single pass that validates root_module refs, depends_on, and imports per item. Extract the repeated install-artifact-and-register pattern into installAndRegister helper used by all three artifact creators. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use toComptimeString consistently for extracting strings from ZON tuples (wireDependsOnList, wireModuleImports). Have createRun reuse wireDependsOnList instead of duplicating the depends_on logic inline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace runtime splitScalar with comptime comptimeBaseName/comptimeAfterSep for link_libraries colon syntax. Also use toComptimeString so link_libraries accepts enum literals (consistent with imports and depends_on). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
configureBuild now takes a comptime Options struct with a help_step
field (default: "help"). When set, zig build help prints a formatted
overview of the project's modules, artifacts, tests, runs, options,
and dependencies — all derived from the manifest at comptime.
The Options struct provides a natural extension point for future
configuration without breaking the API.
Breaking: configureBuild signature changed to accept a third opts
parameter. Callers must add .{} as the third argument.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
B+C hybrid approach: README as landing page, docs/ for schema reference and motivation, examples/ as compilable annotated projects. Nukes all stale docs from the CLI tool era. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add missing fields: zig_lib_dir/win32_manifest for libraries, zig_lib_dir for objects, passthrough/zig_lib_dir for tests, build-test step. Clarify target/optimize value types, link_libraries vs LazyPath colon syntax, LazyPath three-part form, inline module name override, help step metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7 tasks: delete stale docs, write README/motivation/schema, create simple and full compilable examples, delete superpowers working docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete MOTIVATION.md, TODO.md, AdvancedFeatures.md, and STRUCTURAL_ISSUES.md — all describe the old CLI tool architecture that no longer exists. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pitch, before/after comparison, quickstart, feature list, and links to schema reference and examples. Replaces the stale CLI tool README. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers the problem (build.zig verbosity), the insight (@import + comptime), before/after comparison, and what zbuild is NOT. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Field tables for all manifest sections: modules, executables, libraries, objects, tests, fmts, runs, options_modules, dependencies. Plus LazyPath resolution, comptime validation, and configureBuild options. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix bug where three BuildResult getters referenced self.result.X instead of self.X. Propagate OOM errors consistently — createModule, createRun, createFmt now return Error instead of panicking. Extract ~180 lines of help text generation into src/help.zig, keeping build_runner.zig focused on build graph construction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
depends_on entries can now reference any named step using colon syntax: "test:unit", "build-test:unit", "cmd:deploy", "fmt:src", "run:myapp", "build-exe:myapp", "build-lib:mylib", "build-obj:myobj". Plain names (enum literals or strings without colons) continue to reference artifact install steps as before. Comptime validation maps step prefixes to manifest sections. Runtime wiring looks up b.top_level_steps for colon-form references. This makes tests, runs, and fmts valid depends_on targets — previously only executables, libraries, and objects worked. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move buildHelpText, comptimePad, describeValue tests to help.zig where the functions live. build_runner.zig keeps 20 tests, help.zig has 4. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r tests - build.zig imports build_runner.zig directly (skip main.zig passthrough) - Version bump to 0.4.0 (breaking API changes since 0.3.0) - wireDependsOn now handles tests via top_level_steps lookup - Delete leftover docs/specs/ - Update schema.md: depends_on step reference syntax, LazyPath collision note Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
main.zig was a pure passthrough with no added value. build_runner.zig is now the root source file for both the zbuild module and tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request represents a fundamental shift in zbuild's architecture, transforming it from a CLI-driven code generator into a powerful, declarative comptime library for Zig projects. The change leverages Zig's Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This is an excellent and significant rewrite of zbuild. The move from a CLI tool with codegen to a comptime library is a huge architectural improvement, leveraging Zig's comptime capabilities to provide a more robust, declarative, and user-friendly build configuration experience. The code quality is high, and the new documentation is thorough and clear. I have one suggestion to improve the internal structure by centralizing some logic, but overall this is a fantastic contribution.
Move runs depends_on wiring from createRun into wireDependsOn, alongside artifacts and tests. All dependency wiring now happens in Phase 11, keeping creation phases focused on creation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`configureBuild` previously resolved every dependency that lacked an
explicit `.args` field via `b.dependency(name, .{})`. Empty args mean
the child build's `b.standardOptimizeOption` and `b.standardTargetOptions`
fall back to their own defaults (host target, Debug) regardless of the
parent's CLI flags.
For C-heavy deps this is a real footgun: in Debug, Zig's default
`sanitize_c=.full` emits `__ubsan_handle_*` external calls into the
dep's object files. When the parent links those objects into a shared
library (e.g. a Node NAPI `.node`), the symbols stay unresolved and the
library fails to dlopen at runtime despite `-Doptimize=ReleaseSafe`
being passed to the top-level `zig build`.
Forward `runner.target` and `runner.optimize` by default so child
builds inherit the parent's CLI resolution. Users who need full control
(e.g. pin a dep to a specific optimize) can still supply `.args`
explicitly; in that case zbuild passes their args through unchanged
without injecting anything.
Tested with:
- zig build test (26/26)
- zig build test:fixtures (all passing)
Tests that link against C symbols which the runtime resolves at dlopen time (e.g. napi C symbols Node provides when loading a `.node` file) need this flag so the linker doesn't fail standalone test binaries. Previously the option was only allowed for libraries; mirror the same behaviour for tests: - `isKnownArtifactField`: accept `linker_allow_shlib_undefined` in `tests` in addition to `libraries`. - `createTest`: copy the field onto the resulting Compile artifact post-create, mirroring `createLibrary`. - Extend the `stdlib_passthrough` fixture to set the field on both the library and the test entry, exercising both code paths. - Document the field on the `tests` schema table.
Summary
This PR rewrites zbuild from a CLI/codegen tool into a comptime Zig library that configures
std.Builddirectly frombuild.zig.zon.Major changes:
configureBuild(b, @import("build.zig.zon"), ...)0.16.0and updates examples/docs to use direct@import("build.zig.zon")BuildResultfromconfigureBuildaliasesfor named aggregate top-level stepsSemantics tightened in this branch
This branch also hardens the model substantially beyond the initial rewrite:
"dep:module"options_modulesnow generate typed config modules, namespaced-Dmodule.optionflags, real enum types, and explicit optional fields when no default is provideddepends_onis explicit: enum literals mean artifact install-step shorthand, strings mean exact top-level step namesBreaking changes / migration notes
zbuild.zonand the old CLI workflow are gone; use standardbuild.zig.zon0.16.0+is now required0.16.0package-import path directly; the pre-release workaround is removed"dep:module"for dependency submodulesTest plan
zig build test --summary allzig build test:fixtures --summary allcd examples/simple && zig buildcd examples/full && zig buildcd examples/full && zig build infocd test/fixtures/aliases_nested && zig build checkThe fixture suite covers the examples plus targeted negative cases for namespace collisions, dependency import/lazy-path failures, manual interop errors, stdlib passthrough support, and alias validation.