Back to Raylib Rs

WS1 — raylib-sys 6.0 Parity Implementation Plan

docs/superpowers/plans/2026-05-25-ws1-sys-6.0-parity.md

6.0.019.3 KB
Original Source

WS1 — raylib-sys 6.0 Parity Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Make raylib-sys a faithful, verified, cross-platform-green FFI surface for raylib 6.0 — new symbols bound and guarded, feature flags reconciled with 6.0's config.h, hand-defined type layouts proven correct, and the crate building on ubuntu + macOS + windows in CI on the fork.

Architecture: raylib-sys already compiles against raylib 6.0 (WS0 bumped the submodule; bindgen has no allowlist so it regenerated every symbol automatically). WS1 therefore is verification + reconciliation + cross-platform CI, not a rewrite. The safe raylib crate stays red until WS3 — do not touch it here. Work continues on the long-lived 6.0-rc branch.

Tech Stack: Rust 1.85, bindgen + cmake (sys build), GitHub Actions on the fork remote (Dacode45/ms-raylib-rs), gh CLI.

Reference: spec docs/superpowers/specs/2026-05-25-raylib-rs-6.0-roadmap-design.md; WS1 worklist docs/superpowers/notes/ws1-breakage-baseline.md; backlog docs/superpowers/inventory.md.

Owner decision recorded (2026-05-25): push 6.0-rc to the fork now to activate CI, and pull a slice of WS6 forward — expand CI to a 3-OS raylib-sys build. This refines the timing of D4/D6/D7; it does not change D5 (canonical merge still only at WS8). Web/wasm verification stays in WS6 (the emscripten spike found local Windows emsdk needs Python; run web CI on Linux).

Pre-flight:

  • git rev-parse --abbrev-ref HEAD6.0-rc.
  • git remote get-url forkhttps://github.com/Dacode45/ms-raylib-rs.git.
  • git -C raylib-sys/raylib describe --tags → starts with 6.0.
  • cargo build -p raylib-sys → succeeds (Windows baseline).

File structure (created / modified)

PathResponsibilityTask
raylib-sys/tests/symbol_presence.rsCompile/link-time guard that key new 6.0 symbols are bound (create)1
raylib-sys/tests/layout_compat.rsStatic assertions that the 7 hand-defined types match raylib 6.0 layout (create)2
raylib-sys/build.rsfeatures_from_env: reconcile SUPPORT_* defines with 6.0 config.h (modify)3
raylib-sys/Cargo.tomlFeature list reconciled with 6.0 (modify)3
raylib/Cargo.tomlMirror the reconciled feature list (modify)3
docs/superpowers/notes/ws1-config-reconcile.mdRecord of added/removed/renamed flags + rationale (create)3
raylib-sys/build.rs (+ maybe Cargo.toml)Incorporate PR #279 (links/include path export) with attribution (modify)4
.github/workflows/baseline.ymlExpand build-sys to ubuntu+macOS+windows matrix (modify)5

Task 1: Guard the new 6.0 symbols with a presence test

Files: Create raylib-sys/tests/symbol_presence.rs

Rationale: bindgen has no allowlist, so 6.0's new symbols (+40 file, +30 text fns, the redesigned model types) are generated automatically. The risk is a future bindgen-config change silently dropping them. A test that merely references each symbol fails to compile/link if the symbol disappears — a cheap regression guard. We reference, never call (no window needed).

  • Step 1: Write the test raylib-sys/tests/symbol_presence.rs:
rust
//! Compile/link-time guard: these raylib 6.0 symbols must remain bound.
//! Referencing (not calling) each symbol fails the build if bindgen ever drops it.
#![allow(unused, clippy::no_effect)]
use raylib_sys::*;

#[test]
fn new_6_0_functions_are_bound() {
    // Filesystem additions (6.0 folded ~40 fns into rcore)
    let _ = GetFileLength as *const ();
    let _ = MakeDirectory as *const ();
    let _ = LoadDirectoryFiles as *const ();
    // Model animation redesign
    let _ = UpdateModelAnimation as *const ();
    let _ = UnloadModelAnimations as *const ();
}

#[test]
fn new_6_0_types_exist() {
    // Skeletal-animation redesign types
    let _: Option<ModelSkeleton> = None;
    // ModelAnimPose is `typedef Transform *ModelAnimPose` in 6.0
    let _: Option<ModelAnimPose> = None;
}
  • Step 2: Verify it compiles and links (it should — symbols exist)

Run: cargo test -p raylib-sys --test symbol_presence -- --list Expected: lists new_6_0_functions_are_bound and new_6_0_types_exist (compiling + linking the test binary proves the symbols resolve). If any symbol name doesn't resolve, it's either renamed in 6.0 or genuinely absent — fix the reference to the real 6.0 name (check raylib-sys/raylib/src/raylib.h) rather than deleting the guard.

  • Step 3: Run the tests

Run: cargo test -p raylib-sys --test symbol_presence Expected: 2 passed (the bodies do nothing at runtime; this confirms the link).

  • Step 4: Commit
bash
git add raylib-sys/tests/symbol_presence.rs
git commit -m "$(printf 'test(ws1): guard new raylib 6.0 symbols are bound\n\nCompile/link-time references to new file, text, and model-animation\nsymbols so a future bindgen-config change cannot silently drop them.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"

Task 2: Prove the hand-defined types match raylib 6.0 layout

Files: Create raylib-sys/tests/layout_compat.rs

Rationale: build.rs blocklists 7 types from bindgen (Vector2/3/4, Matrix, Quaternion, Rectangle, Color) because they're hand-defined in raylib-sys/src/math.rs and color.rs. If 6.0 changed any of their layouts, our hand definitions would be silently wrong (UB across FFI). Static size/align assertions catch that. (These values are the documented raylib layout; they did not change in 6.0, but the test makes that a guarantee, not an assumption.)

  • Step 1: Write the test raylib-sys/tests/layout_compat.rs:
rust
//! Static guarantee that hand-defined (bindgen-blocklisted) types keep the exact
//! C layout raylib 6.0 expects. A mismatch here is undefined behavior across FFI.
use raylib_sys::{Color, Matrix, Quaternion, Rectangle, Vector2, Vector3, Vector4};
use std::mem::{align_of, size_of};

const _: () = assert!(size_of::<Vector2>() == 8 && align_of::<Vector2>() == 4);
const _: () = assert!(size_of::<Vector3>() == 12 && align_of::<Vector3>() == 4);
const _: () = assert!(size_of::<Vector4>() == 16 && align_of::<Vector4>() == 4);
const _: () = assert!(size_of::<Quaternion>() == 16 && align_of::<Quaternion>() == 4);
const _: () = assert!(size_of::<Matrix>() == 64 && align_of::<Matrix>() == 4);
const _: () = assert!(size_of::<Rectangle>() == 16 && align_of::<Rectangle>() == 4);
const _: () = assert!(size_of::<Color>() == 4 && align_of::<Color>() == 1);

#[test]
fn layouts_match_raylib_6_0() {
    // The const _ assertions above run at compile time; this test documents intent
    // and gives a named target. If raylib 6.0 ever changes a layout, the build fails above.
}
  • Step 2: Verify it compiles (const assertions evaluate at build time)

Run: cargo test -p raylib-sys --test layout_compat Expected: PASS. If a const _ assertion fails, the COMPILE fails with the failing assert — that means raylib 6.0 changed a layout and raylib-sys/src/math.rs/color.rs must be updated to match (do that, then re-run). Cross-check the field set against raylib-sys/raylib/src/raylib.h.

  • Step 3: Commit
bash
git add raylib-sys/tests/layout_compat.rs
git commit -m "$(printf 'test(ws1): assert hand-defined FFI types match raylib 6.0 layout\n\nStatic size/align checks for the 7 bindgen-blocklisted types (Vector*/\nMatrix/Quaternion/Rectangle/Color). A layout drift is FFI UB; this makes\nit a build error.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"

Task 3: Reconcile feature flags with raylib 6.0 config.h

Files: Modify raylib-sys/build.rs (features_from_env), raylib-sys/Cargo.toml, raylib/Cargo.toml; create docs/superpowers/notes/ws1-config-reconcile.md

Rationale (spec: "config.h flag changes reflected in build.rs"): raylib 6.0 redesigned config.h (now 55 SUPPORT_* flags). Our features_from_env in build.rs maps each Cargo SUPPORT_* feature to a cmake define, and both raylib-sys/Cargo.toml and raylib/Cargo.toml list these features. Any flag 6.0 added/removed/renamed must be reflected, or we silently pass dead defines or miss real ones.

  • Step 1: Produce the authoritative 6.0 flag set

Run:

bash
grep -oE "SUPPORT_[A-Z0-9_]+" raylib-sys/raylib/src/config.h | sort -u > /tmp/raylib60-flags.txt
wc -l /tmp/raylib60-flags.txt   # ~55

(On Windows write to a writable temp path instead of /tmp.) Also skim raylib-sys/raylib/src/config.h for any NEW flags grouped by module, and check the CMake side: raylib-sys/raylib/cmake/ / CMakeLists.txt for options exposed under CUSTOMIZE_BUILD.

  • Step 2: Extract our current flag set

Run:

bash
grep -oE 'SUPPORT_[A-Z0-9_]+' raylib-sys/build.rs | sort -u > /tmp/ours-buildrs.txt
grep -oE 'SUPPORT_[A-Z0-9_]+' raylib-sys/Cargo.toml | sort -u > /tmp/ours-sys.txt
diff /tmp/raylib60-flags.txt /tmp/ours-buildrs.txt

The diff shows: lines only in raylib 6.0 (we're MISSING — candidate to add) and lines only in ours (raylib 6.0 DROPPED or renamed — candidate to remove/deprecate).

  • Step 3: Apply reconciliation rules. For each difference:

    • Flag in 6.0 but not ours → add it: a feature entry in raylib-sys/Cargo.toml [features], a mirrored entry in raylib/Cargo.toml (forwarding raylib-sys/<FLAG>), and a cmake.define("<FLAG>", bstr(cfg!(feature = "<FLAG>"))); line in features_from_env. Match the existing surrounding style exactly.
    • Flag in ours but removed in 6.0 → remove the cmake.define line and the [features] entries from BOTH Cargo.tomls. (These are build-time toggles, not public API, so removal is safe; note it.)
    • Renamed → update the define name and the feature names in both manifests.
    • Keep raylib-sys/Cargo.toml and raylib/Cargo.toml feature lists in sync (raylib's forward to raylib-sys/*), as they are today.
  • Step 4: Record the reconciliation in docs/superpowers/notes/ws1-config-reconcile.md:

markdown
# WS1 — config.h feature-flag reconciliation (raylib 5.6-dev → 6.0)
## Added
- <FLAG><module/what it gates>
## Removed (gone in 6.0)
- <FLAG><why removed>
## Renamed
- <OLD> -> <NEW>
## Unchanged
- (the rest)
  • Step 5: Verify default build still green

Run: cargo build -p raylib-sys Expected: success. Then cargo build -p raylib-sys --no-default-features — expected: success (or a documented, intentional failure if a non-optional flag is required; note it).

  • Step 6: Verify the two manifests agree

Run:

bash
diff <(grep -oE 'SUPPORT_[A-Z0-9_]+' raylib-sys/Cargo.toml | sort -u) \
     <(grep -oE 'SUPPORT_[A-Z0-9_]+' raylib/Cargo.toml | sort -u)

Expected: no differences (every sys SUPPORT flag is mirrored in raylib).

  • Step 7: Commit
bash
git add raylib-sys/build.rs raylib-sys/Cargo.toml raylib/Cargo.toml docs/superpowers/notes/ws1-config-reconcile.md
git commit -m "$(printf 'feat(ws1)!: reconcile SUPPORT_* feature flags with raylib 6.0 config.h\n\nAdd new 6.0 flags, drop flags removed in 6.0, fix renames; keep raylib and\nraylib-sys manifests in sync. Reconciliation recorded in ws1-config-reconcile.md.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"

Files: Modify raylib-sys/build.rs (and possibly raylib-sys/Cargo.toml)

Rationale (inventory.md, tagged merge → WS1): PR #279 (themkat, branch feature/links-include-var) adds a links setting to raylib-sys and exports the include path for downstream sys-crate composition. Small and correct; incorporate it now with author credit.

  • Step 1: Inspect the PR

Run: gh pr view 279 --repo raylib-rs/raylib-rs and gh pr diff 279 --repo raylib-rs/raylib-rs Read what it changes (expected: build.rs emits cargo:include=<path> / link metadata, possibly an env-driven extra-links setting).

  • Step 2: Apply the change, adapting to the current 6.0 build.rs structure. Keep it minimal and matching surrounding style. (Re-author rather than raw-cherry-pick if the branch is based on an old tree; preserve intent.)

  • Step 3: Verify the build still succeeds and the metadata is emitted

Run: cargo build -p raylib-sys -vv 2>&1 | grep -iE "cargo:include|cargo:rustc-link" | head Expected: the new cargo:include=... (and/or link) line appears.

  • Step 4: Commit with attribution
bash
git add raylib-sys/build.rs raylib-sys/Cargo.toml
git commit -m "$(printf 'feat(ws1): export raylib-sys include path + links setting\n\nIncorporates PR #279 for downstream sys-crate composition.\n\nCo-authored-by: themkat <[email protected]>\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"

(Confirm themkat's exact git identity from gh pr view 279 --json author and use it in the Co-authored-by line so the credit is real.)


Task 5: Expand CI to a 3-OS raylib-sys build matrix

Files: Modify .github/workflows/baseline.yml

Rationale (owner decision): pull a slice of WS6 forward so the fork's CI builds raylib-sys on all three desktop OSes, giving continuous cross-platform feedback during WS1+. Web stays in WS6.

  • Step 1: Rewrite the build-sys job as an OS matrix. Replace the single build-sys job in .github/workflows/baseline.yml with:
yaml
  build-sys:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - name: Install Linux build deps
        if: runner.os == 'Linux'
        run: sudo apt-get update && sudo apt-get install --no-install-recommends -y cmake libglfw3-dev libasound2-dev libudev-dev
      - name: Install macOS build deps
        if: runner.os == 'macOS'
        run: brew install cmake
      # Windows runners ship cmake + MSVC; no extra deps needed.
      - name: Build raylib-sys
        run: cargo build -p raylib-sys

(Leave the fmt job unchanged.)

  • Step 2: Validate the YAML (Node or any available parser, as in WS0): confirm it parses and has fmt + build-sys jobs with a 3-entry matrix.os.

  • Step 2b: Smoke-test the Linux leg locally with act (if Docker is available). act (run via gh act if the gh extension is installed, else the act binary) executes the workflow in local Docker containers — this catches dependency/step mistakes in the ubuntu-latest legs without a push. It only emulates Linux, so it covers the fmt job and the build-sys (ubuntu-latest) leg; macOS/windows still need real runners (Task 6).

    • Run: gh act -W .github/workflows/baseline.yml -j build-sys --matrix os:ubuntu-latest (or act -j build-sys). First run prompts for a runner image — pick the medium/catthehacker/ubuntu image.
    • Expected: the Linux build-sys leg runs and builds raylib-sys (or surfaces a missing-dep error to fix in the workflow now).
    • If Docker/act is not installed, skip this step and rely on Task 6's real CI run — note that you skipped it.
  • Step 3: Commit

bash
git add .github/workflows/baseline.yml
git commit -m "$(printf 'ci(ws1): build raylib-sys on ubuntu+macos+windows matrix\n\nPulls a WS6 slice forward for continuous cross-platform feedback on the\nfork. Web/wasm stays in WS6.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"

Task 6: Push to the fork and drive the 3-OS matrix to green

Files: none (CI orchestration); may loop back to build.rs/workflow if a platform fails.

Rationale: this is where "compiles clean on all desktop platforms" is actually proven. It is iterative (push → observe → fix → repeat) and controller-orchestrated rather than a single TDD subagent — CI feedback loops don't fit the implementer/review cadence. (Pre-validate the Linux leg with act per Task 5 Step 2b to cut cycles; only macOS/windows truly need this push.)

  • Step 1: Push 6.0-rc to the fork

Run: git push -u fork 6.0-rc Expected: branch created on Dacode45/ms-raylib-rs; push succeeds. (This is the first push — an outward action, already authorized by the owner on 2026-05-25.)

  • Step 2: Watch the run

Run: gh run list --repo Dacode45/ms-raylib-rs --branch 6.0-rc --limit 1 then gh run watch <run-id> --repo Dacode45/ms-raylib-rs (or poll gh run view <run-id>). Expected: fmt green; build-sys (ubuntu/macos/windows) either green or with specific per-OS errors.

  • Step 3: Fix per-OS failures until green. Likely culprits and fixes:

    • macOS: the MAX_MATERIAL_MAPS = 12 override in raylib-sys/src/lib.rs:13 is macOS-only — confirm it still matches 6.0 (grep MAX_MATERIAL_MAPS raylib-sys/raylib/src/*.h). Missing frameworks/link flags surface here.
    • Linux: missing system lib in the apt step (add to the workflow).
    • Windows: already known-green locally. For each failure: make the minimal fix (workflow dep step or build.rs platform arm), commit with a ci(ws1):/fix(ws1): message + co-author trailer, push, re-watch. Repeat until all three matrix legs are green.
  • Step 4: Confirm green end-state

Run: gh run list --repo Dacode45/ms-raylib-rs --branch 6.0-rc --limit 1 Expected: latest run completed / success with all build-sys matrix legs + fmt passing.


Done criteria (WS1 complete when all true)

  • symbol_presence.rs and layout_compat.rs exist, compile, and pass.
  • SUPPORT_* flags reconciled with 6.0 config.h; raylib-sys and raylib manifests in sync; reconciliation documented; default + --no-default-features build states known/green.
  • PR #279 incorporated with real author attribution.
  • baseline.yml builds raylib-sys on ubuntu+macOS+windows.
  • 6.0-rc pushed to the fork; the 3-OS matrix + fmt are green on Actions.
  • The safe raylib crate was NOT modified (still WS3's job); commits grouped under (ws1).

Self-review (against the spec)

  • Spec coverage (WS1 line items): "bindgen regenerated" → done in WS0, guarded by Task 1. "new file/text/rlsw/memory symbols present" → Task 1 presence test. "redesigned skeletal-animation structs land with correct layout" → auto-generated (verified) + Task 2 guards the hand-defined types. "config.h flag changes reflected in build.rs" → Task 3. "compiles clean on all 4 platforms" → Tasks 5–6 cover the 3 desktop OSes; Web is explicitly deferred to WS6 per the emscripten spike (documented, not dropped).
  • Scope boundary: WS1 does not touch raylib/src/ — the 37 safe-crate errors are WS3, after the WS2 math decouple. Stated in every task.
  • Data-driven tasks (3, 6): no fabricated flag names or fix code — the plan gives the exact diff method, files, and decision rules, since the precise set depends on the live config.h diff and CI output (legitimately not knowable until run).
  • Real symbols/types: Task 1/2 reference verified 6.0 symbols (GetFileLength, MakeDirectory, LoadDirectoryFiles, UpdateModelAnimation, UnloadModelAnimations, ModelSkeleton, ModelAnimPose) and documented layouts.
  • Web caveat: "all 4 platforms" is honestly split — 3 desktop in WS1, Web in WS6 — matching the spike's Linux-CI recommendation rather than pretending Web is verified here.