Skip to content

feat(#386): flip sokol → labelle-sokol — assembler now fully backend-agnostic - #452

Merged
apotema merged 1 commit into
mainfrom
feat/386-flip-sokol
Jun 30, 2026
Merged

feat(#386): flip sokol → labelle-sokol — assembler now fully backend-agnostic#452
apotema merged 1 commit into
mainfrom
feat/386-flip-sokol

Conversation

@apotema

@apotema apotema commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

The final backend extraction (#386). Flips builtinProvider(.sokol) from null to the published labelle-sokol@0.1.0. With this, every backend (bgfx, wgpu, null, sdl, raylib, sokol) resolves to an external package — builtinProvider has zero => null arms and the assembler's production codegen is 100% backend-agnostic.

Design note — backends/sokol is retained as a test fixture

Because sokol is the last backend, the ~24 generic codegen tests (which call generateBuildZig and inspect the generated string, reading the backend manifest offline) need some real in-tree manifest. backends/sokol stays purely as the reference/test fixture, pinned explicitly via .backend_package = local:backends/sokol + .project_dir = ".". Production never bundles it — it resolves the external package like every other backend. This keeps CI offline/fast without compromising agnosticism.

Changes (9 files, +133/−111)

  • src/config.zig.sokol => labelle-sokol@0.1.0; refreshed the builtinProvider doc (no null arms remain).
  • src/root.zig — export BuildZigOptions for the typed test helper.
  • test/helpers.zig — new genSokolBuildZig helper injecting the fixture backend_package + project_dir.
  • test/build_zig_tests.zig — 24 sokol call sites → h.genSokolBuildZig (bgfx + error-expecting calls untouched). Splice renders byte-identically, so all string assertions still hold.
  • src/backend_registry.zig, src/deps_linker.zig — rewrote enum-logic tests that assumed sokol was bundled/non-external (now impossible — all backends external).
  • src/codegen/manifest_splice.zig — repointed the built-in no-op test to verify the retained fixture's manifest is accepted.
  • test/main_zig_tests.zig — dropped a now-unreachable "built-in emits no contract guard" test (replaced with a NOTE).
  • test/flow_scanner/integration_tests.zig — 2 sokol generations → fixture.

zig build test and zig build both exit 0 locally. Completes the pluggable-backends epic #386.

https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw

Summary by CodeRabbit

  • Bug Fixes

    • Built-in backend handling now consistently resolves as external, including the sokol backend.
    • Generated build files and manifest checks now work correctly with the retained backend fixture and updated backend resolution behavior.
    • Gamepad and backend wiring logic now applies consistently across all built-in backend configurations.
  • Tests

    • Updated test coverage to match the new backend resolution and build generation behavior.

…end-agnostic

Sokol was the last bundled backend. `builtinProvider(.sokol)` now resolves to
the published `labelle-sokol@0.1.0` package, so every Backend enum tag maps to
an external provider and there are NO remaining `=> null` arms. Production
codegen no longer bundles any backend.

`backends/sokol` is RETAINED purely as the offline in-tree test fixture /
reference manifest (the generic codegen tests need a real backend manifest
readable on disk). Tests that generate with sokol are repointed at the fixture
via an explicit `.backend_package = .{ .name = "sokol", .repo =
"local:backends/sokol" }` + `project_dir = "."`; the manifest splice renders
byte-identically to the pre-flip enum path.

- src/config.zig: flip `.sokol => null` to the labelle-sokol provider; refresh
  the now-stale `builtinProvider` doc (no null arms remain).
- src/root.zig: export `BuildZigOptions` for the test helper.
- test/helpers.zig: add `sokol_fixture_package` + `genSokolBuildZig` helper;
  repoint the 24 sokol build_zig_tests call sites to it.
- src/backend_registry.zig, src/deps_linker.zig: rewrite enum-logic tests that
  asserted sokol was "bundled/non-external" to the new all-external reality
  (the built-in-staging switch arms are now defensive dead code).
- src/codegen/manifest_splice.zig: repoint the built-in no-op test to verify
  the retained fixture's manifest is accepted.
- test/main_zig_tests.zig: drop the "built-in emits NO contract guard" test
  (the no-guard branch is unreachable now every backend is external) with a
  NOTE; external-guard coverage already lives directly above.
- test/flow_scanner/integration_tests.zig: repoint the two sokol generations
  at the fixture.

CI unchanged: the `backends/sokol && zig build test` fixture self-test stays;
no sokol example-integration jobs exist to remove.

Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The built-in sokol backend is changed from a bundled (non-external) backend to an external provider package via builtinProvider() in config.zig. Tests across backend registry, manifest splice, dependency linker, build.zig generation, and integration suites are updated to reflect that all built-in backends now resolve as external. A new BuildZigOptions re-export and genSokolBuildZig test helper support fixture-based test generation.

Changes

Sokol external backend flip

Layer / File(s) Summary
Core builtinProvider resolution change
src/config.zig
builtinProvider(.sokol) now returns a provider package descriptor (labelle-sokol v0.1.0) instead of null, making .sokol external via effectiveBackendPackage().
Backend registry and dependency linker test updates
src/backend_registry.zig, src/deps_linker.zig
Tests now assert every built-in backend enum tag is external, has non-null effectiveBackendPackage(), matches backendName(), and never stages SDL/Android gamepad support.
Manifest splice and contract guard test updates
src/codegen/manifest_splice.zig, test/main_zig_tests.zig
requireManifestIfExternal test now uses sokol via backend_package and project_dir="."; the "no contract guard for built-ins" test is removed with explanatory comments about the now-unreachable branch.
Sokol fixture helper for build.zig generation
src/root.zig, test/helpers.zig
Adds sokol_fixture_package and genSokolBuildZig helper that injects the in-tree sokol fixture and project_dir="." into generation calls; BuildZigOptions is re-exported from root.zig.
build.zig and integration test wiring updates
test/build_zig_tests.zig, test/flow_scanner/integration_tests.zig
Test fixtures switch to h.genSokolBuildZig; integration tests pass explicit backend_package and project_dir="." for sokol-based configs.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Code
    participant Config as ProjectConfig
    participant Resolver as effectiveBackendPackage
    participant Provider as builtinProvider
    participant Manifest as requireManifestIfExternal

    Test->>Config: set backend = .sokol
    Config->>Resolver: effectiveBackendPackage()
    Resolver->>Provider: builtinProvider(.sokol)
    Provider-->>Resolver: provider package (labelle-sokol)
    Resolver-->>Config: non-null package, isExternal = true
    Test->>Manifest: requireManifestIfExternal(project_dir=".")
    Manifest-->>Test: success (manifest found via backend_package)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

A bundle once held sokol tight,
Now it hops out into the light! 🐇
External package, manifest in tow,
Through effectiveBackendPackage it flows.
Tests all updated, every tag in line —
This rabbit's burrow builds just fine. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: sokol is flipped to labelle-sokol and backend generation becomes fully backend-agnostic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/386-flip-sokol

Comment @coderabbitai help to get the list of available commands.

@apotema
apotema merged commit 2eb7fbd into main Jun 30, 2026
3 of 4 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2376028026

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/config.zig
.raylib => .{ .name = "raylib", .repo = "github.com/labelle-toolkit/labelle-raylib", .version = "0.1.0" },
// Still bundled.
.sokol => null,
.sokol => .{ .name = "sokol", .repo = "github.com/labelle-toolkit/labelle-sokol", .version = "0.1.0" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep Sokol web/iOS on a supported path

Flipping the bare Sokol enum to an external provider makes cfg.isExternal() true for every .backend = .sokol project. On .platform = .wasm or .ios, the manifest path is disabled because manifests are desktop-only, and generateBuildZig only allows external enum fallback on Android (externalUsesEnumPath), so generation returns ExternalBackendNeedsManifest before the existing backend_sokol_wasm / backend_sokol_ios sections can run. This breaks current Sokol web/iOS projects that don't set an explicit backend_package; either keep Sokol non-external for those targets or add manifest/enum support for them before this flip.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
test/main_zig_tests.zig (1)

431-437: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep a positive contract-guard test for the enum-shorthand path.

The remaining assertion only covers an explicit .backend_package. This PR’s actual behavior change is that plain .backend = .<tag> now becomes external via builtinProvider(), so a regression where the guard still keys off cfg.backend_package != null would slip through.

🧪 Suggested test
+    test "enum-shorthand built-in backend emits the core-contract verification guard" {
+        const main_zig = try generate.generateMainZigFromTemplate(std.testing.allocator, engine_template, .{ .y_axis = .up,
+            .name = "test-game",
+            .backend = .raylib,
+            .ecs = .mock,
+        }, raylib_lifecycle, empty_entries, empty_names, empty_names, empty_scene_manifests, empty_names, empty_names, empty_names, empty_names, empty_names, empty_names, empty_names, empty_plugin_events, empty_plugin_flow_nodes, empty_plugin_pin_styles, empty_plugin_coercions);
+        defer std.testing.allocator.free(main_zig);
+
+        try std.testing.expect(std.mem.indexOf(u8, main_zig, "assertBackend(`@import`(\"backend_gfx\"))") != null);
+        try std.testing.expect(std.mem.indexOf(u8, main_zig, "assertWindow(`@import`(\"backend_window\"))") != null);
+        try std.testing.expect(std.mem.indexOf(u8, main_zig, "assertInput(`@import`(\"backend_input\"))") != null);
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main_zig_tests.zig` around lines 431 - 437, Add a positive
contract-guard test that exercises the enum-shorthand `.backend = .<tag>` path,
not just an explicit `.backend_package`, so `main_zig_tests.zig` verifies the
new `builtinProvider()` behavior. Update the relevant test case(s) to assert
that a plain backend tag now resolves as external and still emits the contract
guard, ensuring the check is not accidentally tied only to `cfg.backend_package
!= null`.
test/helpers.zig (1)

351-355: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make genSokolBuildZig force the sokol tag.

Line 352 only rewires backend_package. If a caller forgets .backend = .sokol, this helper still builds a mixed config even though its contract is “generate a sokol build.zig”. Setting the tag here (or asserting it) would make the helper self-consistent.

Proposed change
 pub fn genSokolBuildZig(
     allocator: std.mem.Allocator,
     cfg_in: generate.ProjectConfig,
     opts_in: generate.BuildZigOptions,
 ) ![]const u8 {
     var cfg = cfg_in;
+    cfg.backend = .sokol;
     cfg.backend_package = sokol_fixture_package;
     var opts = opts_in;
     opts.project_dir = ".";
     return generate.generateBuildZig(allocator, cfg, opts);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/helpers.zig` around lines 351 - 355, genSokolBuildZig currently only
swaps backend_package, so it can still produce a mixed config if the caller does
not set the sokol backend explicitly. Update genSokolBuildZig in helpers.zig to
force the cfg.backend tag to .sokol (or assert it before calling
generate.generateBuildZig), alongside the existing backend_package and
opts.project_dir setup, so the helper always matches its sokol-specific
contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/helpers.zig`:
- Around line 351-355: genSokolBuildZig currently only swaps backend_package, so
it can still produce a mixed config if the caller does not set the sokol backend
explicitly. Update genSokolBuildZig in helpers.zig to force the cfg.backend tag
to .sokol (or assert it before calling generate.generateBuildZig), alongside the
existing backend_package and opts.project_dir setup, so the helper always
matches its sokol-specific contract.

In `@test/main_zig_tests.zig`:
- Around line 431-437: Add a positive contract-guard test that exercises the
enum-shorthand `.backend = .<tag>` path, not just an explicit
`.backend_package`, so `main_zig_tests.zig` verifies the new `builtinProvider()`
behavior. Update the relevant test case(s) to assert that a plain backend tag
now resolves as external and still emits the contract guard, ensuring the check
is not accidentally tied only to `cfg.backend_package != null`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 696d8815-bd85-4cdc-96e0-ac7e96883104

📥 Commits

Reviewing files that changed from the base of the PR and between 1eaf0fa and 2376028.

📒 Files selected for processing (9)
  • src/backend_registry.zig
  • src/codegen/manifest_splice.zig
  • src/config.zig
  • src/deps_linker.zig
  • src/root.zig
  • test/build_zig_tests.zig
  • test/flow_scanner/integration_tests.zig
  • test/helpers.zig
  • test/main_zig_tests.zig

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant