mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
Merge pull request #15 from imjasonh/claude/platformer-game-demo-WK8XS
Commit built .nes ROMs alongside .ne sources
This commit is contained in:
commit
59905147b4
26 changed files with 71 additions and 9 deletions
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
|
|
@ -58,12 +58,26 @@ jobs:
|
|||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo build --release
|
||||
- name: Compile all .ne examples
|
||||
- name: Verify committed ROMs match their .ne sources
|
||||
# Every example has a committed .nes file under examples/. The
|
||||
# compiler is deterministic (verified: same input → byte-identical
|
||||
# output), so rebuilding each example into a tmp directory and
|
||||
# diffing against the committed ROM catches "someone edited a .ne
|
||||
# file but forgot to regenerate the ROM" at PR time.
|
||||
run: |
|
||||
mkdir -p /tmp/reprotest
|
||||
fail=0
|
||||
for f in examples/*.ne; do
|
||||
echo "==> Compiling $f"
|
||||
cargo run --release -- build "$f"
|
||||
name=$(basename "$f" .ne)
|
||||
./target/release/nescript build "$f" --output "/tmp/reprotest/$name.nes"
|
||||
if ! cmp -s "examples/$name.nes" "/tmp/reprotest/$name.nes"; then
|
||||
echo "::error file=examples/$name.ne::committed examples/$name.nes is stale; rerun \`cargo run --release -- build examples/$name.ne\` and commit the new ROM"
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
if [ $fail -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
- name: Verify ROMs are valid iNES
|
||||
run: |
|
||||
for f in examples/*.nes; do
|
||||
|
|
@ -113,6 +127,14 @@ jobs:
|
|||
- name: Build compiler
|
||||
run: cargo build --release
|
||||
- name: Compile all .ne examples
|
||||
# Overwrites the committed examples/*.nes in the CI workspace
|
||||
# (the workspace is ephemeral; the committed ROMs aren't
|
||||
# touched). This is load-bearing: the goldens must be diffed
|
||||
# against what *this* compiler produces, not against the ROMs
|
||||
# an earlier commit happened to ship, otherwise a broken
|
||||
# compiler change can still pass the emulator job as long as
|
||||
# the contributor forgot to rebuild the ROMs. The `examples`
|
||||
# job catches that second case via its reproducibility diff.
|
||||
run: |
|
||||
for f in examples/*.ne; do
|
||||
echo "==> Compiling $f"
|
||||
|
|
@ -121,7 +143,7 @@ jobs:
|
|||
- name: Install emulator harness dependencies
|
||||
working-directory: tests/emulator
|
||||
run: npm ci
|
||||
- name: Diff each ROM's framebuffer against its committed golden
|
||||
- name: Diff each ROM's framebuffer against its golden
|
||||
working-directory: tests/emulator
|
||||
run: node run_examples.mjs
|
||||
- name: Upload actual + diff PNGs on failure
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
|||
/target
|
||||
*.nes
|
||||
*.dbg
|
||||
*.map
|
||||
|
|
|
|||
25
CLAUDE.md
25
CLAUDE.md
|
|
@ -15,6 +15,13 @@ re-derive the project conventions from scratch.
|
|||
pipeline.
|
||||
- Examples live in `examples/*.ne`. Every example is expected to compile
|
||||
cleanly and has a pinned emulator golden — see below.
|
||||
- **`examples/*.nes` is committed.** The compiler is deterministic
|
||||
(same source → byte-identical ROM), so the ROMs travel with the
|
||||
repo. If you edit any `.ne` file you **must** rebuild its `.nes`
|
||||
in the same commit — CI's `examples` job rebuilds each ROM into a
|
||||
tmp path and fails if the committed version differs, pointing at
|
||||
the exact `cargo run -- build examples/<name>.ne` to run. The
|
||||
pre-commit hook under `scripts/pre-commit` catches this locally.
|
||||
- `docs/future-work.md` lists the remaining gaps. If you implement
|
||||
something from that file, update the doc in the same PR.
|
||||
|
||||
|
|
@ -63,8 +70,9 @@ tests/emulator/
|
|||
The harness is **separate** from `cargo test`. You have to run it by hand:
|
||||
|
||||
```bash
|
||||
# 1. Build every example first. The harness reads pre-built .nes files
|
||||
# from examples/ — it will not invoke cargo.
|
||||
# 1. Rebuild every example with the current compiler. The harness
|
||||
# reads whatever sits under examples/*.nes — if you want to test
|
||||
# your working copy you have to rebuild them first.
|
||||
cargo build --release
|
||||
for f in examples/*.ne; do ./target/release/nescript build "$f"; done
|
||||
|
||||
|
|
@ -72,13 +80,22 @@ for f in examples/*.ne; do ./target/release/nescript build "$f"; done
|
|||
cd tests/emulator
|
||||
npm install # or `npm ci` in CI
|
||||
|
||||
# 3. Verify every example still matches its golden.
|
||||
# 3. Verify every ROM still matches its golden.
|
||||
node run_examples.mjs
|
||||
# → "21/21 ROMs match their goldens" on success
|
||||
# → "22/22 ROMs match their goldens" on success
|
||||
# → FAIL / MISS lines + `actual/<name>.png`, `actual/<name>.diff.png`,
|
||||
# `actual/<name>.wav` written for any ROM that mismatched
|
||||
```
|
||||
|
||||
The harness always runs against whatever sits in `examples/*.nes`,
|
||||
so iterating on the compiler means rebuilding the example first.
|
||||
CI's `emulator` job does this too — it builds the compiler, compiles
|
||||
every `.ne` into the workspace (overwriting the committed ROMs,
|
||||
which are ephemeral in the CI checkout), and then runs the harness.
|
||||
The committed ROMs are a PR-review convenience and a "did this
|
||||
change affect codegen" tripwire via the `examples` job's
|
||||
reproducibility diff; they are **not** what the emulator job tests.
|
||||
|
||||
### Updating goldens
|
||||
|
||||
If a change is supposed to flip goldens (you added a new example, changed
|
||||
|
|
|
|||
BIN
examples/arrays_and_functions.nes
Normal file
BIN
examples/arrays_and_functions.nes
Normal file
Binary file not shown.
BIN
examples/audio_demo.nes
Normal file
BIN
examples/audio_demo.nes
Normal file
Binary file not shown.
BIN
examples/bitwise_ops.nes
Normal file
BIN
examples/bitwise_ops.nes
Normal file
Binary file not shown.
BIN
examples/bouncing_ball.nes
Normal file
BIN
examples/bouncing_ball.nes
Normal file
Binary file not shown.
BIN
examples/coin_cavern.nes
Normal file
BIN
examples/coin_cavern.nes
Normal file
Binary file not shown.
BIN
examples/comparisons.nes
Normal file
BIN
examples/comparisons.nes
Normal file
Binary file not shown.
BIN
examples/function_chain.nes
Normal file
BIN
examples/function_chain.nes
Normal file
Binary file not shown.
BIN
examples/hello_sprite.nes
Normal file
BIN
examples/hello_sprite.nes
Normal file
Binary file not shown.
BIN
examples/inline_asm_demo.nes
Normal file
BIN
examples/inline_asm_demo.nes
Normal file
Binary file not shown.
BIN
examples/logic_ops.nes
Normal file
BIN
examples/logic_ops.nes
Normal file
Binary file not shown.
BIN
examples/loop_break_continue.nes
Normal file
BIN
examples/loop_break_continue.nes
Normal file
Binary file not shown.
BIN
examples/match_demo.nes
Normal file
BIN
examples/match_demo.nes
Normal file
Binary file not shown.
BIN
examples/mmc1_banked.nes
Normal file
BIN
examples/mmc1_banked.nes
Normal file
Binary file not shown.
BIN
examples/mmc3_per_state_split.nes
Normal file
BIN
examples/mmc3_per_state_split.nes
Normal file
Binary file not shown.
BIN
examples/palette_and_background.nes
Normal file
BIN
examples/palette_and_background.nes
Normal file
Binary file not shown.
BIN
examples/platformer.nes
Normal file
BIN
examples/platformer.nes
Normal file
Binary file not shown.
BIN
examples/scanline_split.nes
Normal file
BIN
examples/scanline_split.nes
Normal file
Binary file not shown.
BIN
examples/sprites_and_palettes.nes
Normal file
BIN
examples/sprites_and_palettes.nes
Normal file
Binary file not shown.
BIN
examples/state_machine.nes
Normal file
BIN
examples/state_machine.nes
Normal file
Binary file not shown.
BIN
examples/structs_enums_for.nes
Normal file
BIN
examples/structs_enums_for.nes
Normal file
Binary file not shown.
BIN
examples/two_player.nes
Normal file
BIN
examples/two_player.nes
Normal file
Binary file not shown.
BIN
examples/uxrom_banked.nes
Normal file
BIN
examples/uxrom_banked.nes
Normal file
Binary file not shown.
|
|
@ -15,4 +15,28 @@ cargo clippy --all-targets -- -D warnings
|
|||
echo " Running tests..."
|
||||
cargo test --all-targets
|
||||
|
||||
# Committed ROMs must match their .ne sources — the compiler is
|
||||
# deterministic, so any example whose on-disk .nes differs from a
|
||||
# fresh build means someone edited the .ne file but forgot to
|
||||
# rebuild. Rebuild each to a tmp path and fail if any differ, then
|
||||
# point the user at the exact `cargo run` command to fix it.
|
||||
echo " Checking committed ROMs match .ne sources..."
|
||||
cargo build --release --quiet
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
stale=0
|
||||
for f in examples/*.ne; do
|
||||
name=$(basename "$f" .ne)
|
||||
./target/release/nescript build "$f" --output "$tmp/$name.nes" >/dev/null
|
||||
if ! cmp -s "examples/$name.nes" "$tmp/$name.nes"; then
|
||||
echo " STALE: examples/$name.nes"
|
||||
echo " rerun: cargo run --release -- build $f"
|
||||
stale=1
|
||||
fi
|
||||
done
|
||||
if [ $stale -ne 0 ]; then
|
||||
echo "one or more committed ROMs are out of date"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All pre-commit checks passed."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue