From 9b915fb2afd7b00b1cd0693777ceb1f5aeeb9a78 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 30 Aug 2026 11:42:34 +0200 Subject: [PATCH 1/2] reworked makefiles - fix staleness/dependency issues - refactored common test rules into common.mk - removed no6502 target - should build and test in parallel with -j8 - assume GNU make 3.81 (macOS) or higher --- CLAUDE.md | 3 +- Makefile | 24 ++++++------- src/6502/Makefile | 8 +++-- src/Makefile | 40 ++++++++++++++------- test/Makefile | 47 +++++++++++++++---------- test/aa-exercise/Makefile | 39 ++++----------------- test/body_not_status/Makefile | 37 +++----------------- test/codepoints/Makefile | 52 +++++++--------------------- test/common.mk | 65 +++++++++++++++++++++++++++++++++++ test/familiar/Makefile | 7 ++-- test/gosling/Makefile | 34 +----------------- test/impossible/Makefile | 35 ++----------------- 12 files changed, 171 insertions(+), 220 deletions(-) create mode 100644 test/common.mk diff --git a/CLAUDE.md b/CLAUDE.md index 16fc88a..53cb330 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,11 +14,10 @@ The build system is plain `make`. The top-level `Makefile` delegates to `src/` a ```sh make # builds aamshow, aambundle, 6502 blobs, then runs tests -make no6502 # builds C tools and tests them without rebuilding 6502 assets make 6502 # builds only the 6502 engine, frontends, and the aambox6502 emulator make windows # cross-compiles .exe versions (needs i686-w64-mingw32-gcc) make test # runs the test suite (requires the C tools to be built) -make clean # removes build outputs +make clean # removes build outputs and test transcripts make install # copies aamshow and aambundle to /usr/local/bin ``` diff --git a/Makefile b/Makefile index 8e83d05..ea4191f 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,21 @@ -BINARIES=src/aamshow src/aambundle +# Full build: the 6502 blobs, then the C tools that embed them, then the tests. +all: tools test -all: $(BINARIES) 6502 test - -src/aamshow: - $(MAKE) -C src - -src/aambundle: +# "make -C src" builds the 6502 side first, then the C tools. +tools: $(MAKE) -C src -windows: - $(MAKE) -C src windows - +# Only the 6502 engine, frontends, and the aambox6502 emulator. 6502: $(MAKE) -C src 6502 -no6502: $(BINARIES) test +windows: + $(MAKE) -C src windows -test: $(BINARIES) +test: tools $(MAKE) -C test -install: $(BINARIES) +install: tools $(MAKE) -C src install tidy: @@ -35,4 +31,4 @@ uninstall: distclean: clean uninstall -.PHONY: all test clean tidy install uninstall distclean windows 6502 no6502 +.PHONY: all tools windows 6502 test clean tidy install uninstall distclean diff --git a/src/6502/Makefile b/src/6502/Makefile index 9c1b938..9678c58 100644 --- a/src/6502/Makefile +++ b/src/6502/Makefile @@ -9,7 +9,6 @@ check_xa: echo " macOS: brew install xa"; \ echo " Debian/Ubuntu: apt install xa65"; \ echo " From source: https://www.floodgap.com/retrotech/xa/"; \ - echo "(To build the C tools without any 6502 code, run 'make no6502' from the top level.)"; \ exit 1; fi check_acme: @@ -18,13 +17,18 @@ check_acme: echo " macOS: brew install acme"; \ echo " Debian/Ubuntu: apt install acme"; \ echo " From source: https://github.com/meonwax/acme"; \ - echo "(Only the Apple II targets need acme; the C64 and aambox targets build with xa alone.)"; \ exit 1; fi clean: rm -rf aambox6502 cruncher labels c64.labels mkfont rm -f a2_frontend.bin a2_prorwts2.bin a2.labels a2.system a2_0boot.bin a2_sboot.bin a2_sboot.labels +mkfont: mkfont.c + ${CC} ${CFLAGS} -o $@ mkfont.c + +cruncher: cruncher.c + ${CC} ${CFLAGS} -o $@ cruncher.c + aambox6502: aambox6502.c fake6502.c aambox_frontend.bin: aambox_frontend.s engine.s | check_xa diff --git a/src/Makefile b/src/Makefile index 8d2bafe..2da6c26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,13 +9,24 @@ endif MINGW32 = i686-w64-mingw32-gcc INSTALLDIR = /usr/local/bin -all: aamshow aambundle - -windows: aamrun.exe aamshow.exe aambundle.exe +# The C tools embed binary blobs produced by the 6502 sub-make (table_*.h). +# GNU make caches a file's timestamp before it runs a sub-make, so a single +# pass cannot tell whether the sub-make actually refreshed a blob. Build the +# 6502 side first, then re-enter make: the second pass stats the blobs fresh +# and regenerates only the headers that really changed. +all: 6502 + $(MAKE) tools 6502: $(MAKE) -C 6502 +tools: aamshow aambundle + +windows: 6502 + $(MAKE) windows-tools + +windows-tools: aamrun.exe aamshow.exe aambundle.exe + tidy: rm -f *.o @@ -23,7 +34,7 @@ clean: tidy rm -f aamshow aambundle aamrun aamshow.exe aambundle.exe aamrun.exe $(MAKE) -C 6502 clean -install: aamshow aambundle +install: all cp aamshow $(INSTALLDIR) cp aambundle $(INSTALLDIR) @@ -91,31 +102,34 @@ aambundle.exe: aambundle.c bundle_web.c bundle_c64.c bundle_apple2.c \ tables_6502font.h ${MINGW32} ${CFLAGS} -o $@ aambundle.c bundle_web.c bundle_c64.c bundle_apple2.c +mkheader: mkheader.c + ${CC} ${CFLAGS} -o $@ mkheader.c + tables_6502font.h: 6502/fontdef.txt mkfontdata ./mkfontdata <6502/fontdef.txt >tables_6502font.h -table_a2terp.h: 6502 6502/a2.system mkheader +table_a2terp.h: 6502/a2.system mkheader ./mkheader table_a2terp <6502/a2.system >$@ -table_a20boot.h: 6502 6502/a2_0boot.bin mkheader +table_a20boot.h: 6502/a2_0boot.bin mkheader ./mkheader table_a20boot <6502/a2_0boot.bin >$@ -table_a2sboot.h: 6502 6502/a2_sboot.bin mkheader +table_a2sboot.h: 6502/a2_sboot.bin mkheader ./mkheader table_a2sboot <6502/a2_sboot.bin >$@ -table_a2license.h: 6502 6502/license.txt 6502/a2_thirdparty_license.txt mkheader +table_a2license.h: 6502/license.txt 6502/a2_thirdparty_license.txt mkheader cat 6502/license.txt 6502/a2_thirdparty_license.txt | ./mkheader table_a2license >$@ -table_c64license.h: 6502 6502/license.txt mkheader +table_c64license.h: 6502/license.txt mkheader ./mkheader table_c64license <6502/license.txt >$@ -table_c64drive.h: 6502 6502/c64_drivecode.bin mkheader +table_c64drive.h: 6502/c64_drivecode.bin mkheader ./mkheader table_c64drive <6502/c64_drivecode.bin >$@ -table_c64load.h: 6502 6502/c64_loader.prg mkheader +table_c64load.h: 6502/c64_loader.prg mkheader ./mkheader table_c64load <6502/c64_loader.prg >$@ -table_c64terp.h: 6502 6502/c64_crunched.bin mkheader +table_c64terp.h: 6502/c64_crunched.bin mkheader ./mkheader table_c64terp <6502/c64_crunched.bin >$@ table_weblicense.h: js/license.txt mkheader @@ -136,4 +150,4 @@ table_jquery.h: js/jquery-3.4.1.min.js mkheader table_play.h: js/webfrontend.html mkheader ./mkheader table_play <$< >$@ -.PHONY: all clean tidy install uninstall distclean windows 6502 +.PHONY: all tools windows windows-tools clean tidy install uninstall distclean 6502 diff --git a/test/Makefile b/test/Makefile index 1869422..5e6ce4a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,22 +1,33 @@ -all: test clean +## Tests: +## gosling - 0.x file using 67 ENTER_STATUS_0 and E7 LEAVE_STATUS +## body_not_status - 1.x file using 67 BODY_STYLE and EF LEAVE_STATUS +## impossible - whatever the latest version of Dialog produces (update periodically) +## codepoints - Character set and status bar exercise; source is in the directory +TESTDIRS = gosling body_not_status impossible codepoints + +TESTS = $(addprefix test-,$(TESTDIRS)) +CLEANS = $(addprefix clean-,$(TESTDIRS)) + +# Transcripts (.out files) are left in place so a second `make` only re-diffs; +# `make clean` (or deleting the .out files) forces a full re-run. +all: test + +test: $(TESTS) + +# Static pattern rules (not plain pattern rules): the targets are named +# explicitly, so they can be declared .PHONY without make refusing to apply +# the rule -- implicit-rule search is skipped for phony targets. +$(TESTS): test-%: + $(MAKE) -C $* test + +# Tests for Apple II interpreter (not run by make test) apple2: $(MAKE) -C apple2 test -test: - ## This one is a 0.x file using 67 ENTER_STATUS_0 and E7 LEAVE_STATUS - make -C gosling test - ## This one is a 1.x file using 67 BODY_STYLE and EF LEAVE_STATUS - make -C body_not_status test - ## This one is whatever the latest version of Dialog produces (update periodically) - make -C impossible test - ## Character set and status bar exercise; source is in the directory - make -C codepoints test - -clean: - $(MAKE) -C gosling clean - $(MAKE) -C body_not_status clean - $(MAKE) -C impossible clean - $(MAKE) -C codepoints clean - -.PHONY: all test apple2 clean +clean: $(CLEANS) + +$(CLEANS): clean-%: + $(MAKE) -C $* clean + +.PHONY: all test apple2 clean $(TESTS) $(CLEANS) diff --git a/test/aa-exercise/Makefile b/test/aa-exercise/Makefile index a62c26c..0de784c 100644 --- a/test/aa-exercise/Makefile +++ b/test/aa-exercise/Makefile @@ -1,37 +1,12 @@ -# Call `make DIFF=meld` to get a fancy diff -DIFF = diff - -DIR6502 = ../../src/6502 -AAMBOX = $(DIR6502)/aambox6502 -FRONTEND = $(DIR6502)/aambox_frontend.bin +# The story source is aavm assembly (aa-exercise.scm). Since the aavm +# assembler lives outside this repo, producing the .aastory is a separate +# `make assemble` step; `make test` (and the top-level test suite) does not +# include this directory. ASSEMBLE = ../../../aavm/aavm.scm -$(AAMBOX): - $(MAKE) -C $(DIR6502) aambox6502 - -$(FRONTEND): - $(MAKE) -C $(DIR6502) aambox_frontend.bin +include ../common.mk assemble: - $(ASSEMBLE) -S aa-exercise.scm aa-exercise.aastory - -all: test - -test: test.js test.6502 - -test.js: aa-exercise.js.out - $(DIFF) aa-exercise.js.out aa-exercise.js.gold - -aa-exercise.js.out: aa-exercise.aastory - node ../../src/js/nodefrontend.js -s 1234 aa-exercise.aastory aa-exercise.js.out - -test.6502: aa-exercise.6502.out - $(DIFF) aa-exercise.6502.out aa-exercise.6502.gold - -aa-exercise.6502.out: aa-exercise.aastory $(AAMBOX) $(FRONTEND) - $(AAMBOX) -s 1234 $(FRONTEND) aa-exercise.aastory aa-exercise.6502.out - -clean: - rm -f *.out + $(ASSEMBLE) -S $(STORY).scm $(STORY).aastory -.PHONY: all test test.js test.6502 clean +.PHONY: assemble \ No newline at end of file diff --git a/test/body_not_status/Makefile b/test/body_not_status/Makefile index b7c356b..a6e85da 100644 --- a/test/body_not_status/Makefile +++ b/test/body_not_status/Makefile @@ -1,33 +1,6 @@ -# Call `make DIFF=meld` to get a fancy diff -DIFF = diff +# The JS and 6502 engines must produce identical output here, so both +# engines diff against the same gold file. +JS_GOLD = $(STORY).gold +GOLD6502 = $(STORY).gold -DIR6502 = ../../src/6502 -AAMBOX = $(DIR6502)/aambox6502 -FRONTEND = $(DIR6502)/aambox_frontend.bin - -$(AAMBOX): - $(MAKE) -C $(DIR6502) aambox6502 - -$(FRONTEND): - $(MAKE) -C $(DIR6502) aambox_frontend.bin - -all: test - -test: test.js test.6502 - -test.js: body_not_status.js.out - $(DIFF) body_not_status.js.out body_not_status.gold - -body_not_status.js.out: body_not_status.aastory - node ../../src/js/nodefrontend.js -s 1234 body_not_status.aastory body_not_status.js.out - -test.6502: body_not_status.6502.out - $(DIFF) body_not_status.6502.out body_not_status.gold - -body_not_status.6502.out: body_not_status.aastory $(AAMBOX) $(FRONTEND) - $(AAMBOX) -s 1234 $(FRONTEND) body_not_status.aastory body_not_status.6502.out - -clean: - rm -f *.out - -.PHONY: all test test.js test.6502 clean +include ../common.mk \ No newline at end of file diff --git a/test/codepoints/Makefile b/test/codepoints/Makefile index 8cd9295..f21f34c 100644 --- a/test/codepoints/Makefile +++ b/test/codepoints/Makefile @@ -1,39 +1,8 @@ -# Call `make DIFF=meld` to get a fancy diff -DIFF = diff - -DIR6502 = ../../src/6502 -AAMBOX = $(DIR6502)/aambox6502 -FRONTEND = $(DIR6502)/aambox_frontend.bin -AAMBUNDLE = ../../src/aambundle +# The story is compiled from Dialog source with an external compiler. DIALOGC ?= dialogc +AAMBUNDLE = ../../src/aambundle -$(AAMBOX): - $(MAKE) -C $(DIR6502) aambox6502 - -$(FRONTEND): - $(MAKE) -C $(DIR6502) aambox_frontend.bin - -$(AAMBUNDLE): - $(MAKE) -C ../../src aambundle - -all: test - -test: test.js test.6502 - -test.js: codepoints.js.out - $(DIFF) codepoints.js.out codepoints.js.gold - -codepoints.js.out: codepoints.aastory - node ../../src/js/nodefrontend.js -s 1234 codepoints.aastory codepoints.js.out - -test.6502: codepoints.6502.out - $(DIFF) codepoints.6502.out codepoints.6502.gold - -codepoints.6502.out: codepoints.aastory $(AAMBOX) $(FRONTEND) - $(AAMBOX) -s 1234 $(FRONTEND) codepoints.aastory codepoints.6502.out - -codepoints.aastory: codepoints.dg - $(DIALOGC) -t aa -o $@ $< +include ../common.mk # Neither the node frontend nor the aambox frontend renders a status area, and # neither one transliterates -- both emit UTF-8 straight through. So the two @@ -60,11 +29,14 @@ codepoints.aastory: codepoints.dg # neither may the (clear all) that follows it. # (Use +/- to nudge the status bar height for 24/25 row systems.) -disks: codepoints.aastory $(AAMBUNDLE) - $(AAMBUNDLE) -t apple2 -o codepoints codepoints.aastory - $(AAMBUNDLE) -t c64 -o codepoints codepoints.aastory +$(STORY).aastory: $(STORY).dg + $(DIALOGC) -t aa -o $@ $< + +$(AAMBUNDLE): + $(MAKE) -C ../../src aambundle -clean: - rm -f *.out +disks: $(STORY).aastory $(AAMBUNDLE) + $(AAMBUNDLE) -t apple2 -o $(STORY) $(STORY).aastory + $(AAMBUNDLE) -t c64 -o $(STORY) $(STORY).aastory -.PHONY: all test test.js test.6502 disks clean +.PHONY: disks \ No newline at end of file diff --git a/test/common.mk b/test/common.mk new file mode 100644 index 0000000..5affaef --- /dev/null +++ b/test/common.mk @@ -0,0 +1,65 @@ +# Shared rules for the simple JS-vs-6502 comparison test suites. +# +# Each suite lives in a directory named after its story, and holds +# STORY.aastory (or a rule to build it), STORY.in, and transcript gold +# files. A suite's Makefile usually needs only: +# +# include ../common.mk +# +# Everything is driven by a few variables, all with sensible defaults: +# +# STORY basename of the story files; defaults to the directory name +# JS_GOLD gold transcript for the JS engine +# GOLD6502 gold transcript for the 6502 engine +# DIFF diff program, e.g. `make DIFF=meld` (see below) +# +# Most suites keep separate JS and 6502 golds; suites where the two engines +# must produce identical output (body_not_status, impossible) point both +# variables at one file. +# +# Suites that need extra targets (aavm assembly, disk bundling, ...) simply +# add their own rules after the include, and extend .PHONY accordingly. + +# Call `make DIFF=meld` to get a fancy diff +DIFF ?= diff + +STORY ?= $(notdir $(CURDIR)) +JS_GOLD ?= $(STORY).js.gold +GOLD6502 ?= $(STORY).6502.gold + +DIR6502 = ../../src/6502 +AAMBOX = $(DIR6502)/aambox6502 +AAMFRONTEND = $(DIR6502)/aambox_frontend.bin + +JS_ENGINE = ../../src/js/engine.js +JS_FRONTEND = ../../src/js/nodefrontend.js + +# Build the 6502 engine pieces if they are not already built. +$(AAMBOX): $(DIR6502)/aambox6502.c $(DIR6502)/fake6502.c + $(MAKE) -C $(DIR6502) aambox6502 + +$(AAMFRONTEND): $(DIR6502)/aambox_frontend.s $(DIR6502)/engine.s + $(MAKE) -C $(DIR6502) aambox_frontend.bin + +all: test + +test: test.js test.6502 + +# Run the story on the JS engine and diff the transcript. +test.js: $(STORY).js.out + $(DIFF) $(STORY).js.out $(JS_GOLD) + +$(STORY).js.out: $(STORY).aastory $(STORY).in $(JS_ENGINE) $(JS_FRONTEND) + node $(JS_FRONTEND) -s 1234 $< <$(STORY).in >$@ + +# Run the story on the 6502 engine (under the aambox emulator) and diff. +test.6502: $(STORY).6502.out + $(DIFF) $(STORY).6502.out $(GOLD6502) + +$(STORY).6502.out: $(STORY).aastory $(STORY).in $(AAMBOX) $(AAMFRONTEND) + $(AAMBOX) -s 1234 $(AAMFRONTEND) $< <$(STORY).in >$@ + +clean: + rm -f *.out + +.PHONY: all test test.js test.6502 clean diff --git a/test/familiar/Makefile b/test/familiar/Makefile index f20c30e..e5fd652 100644 --- a/test/familiar/Makefile +++ b/test/familiar/Makefile @@ -1,6 +1,9 @@ -DIALOGC=../../../Dialog/src/dialogc +DIALOGC ?= dialogc AAMBUNDLE=../../src/aambundle +$(AAMBUNDLE): + $(MAKE) -C ../../src aambundle + sourcefiles = familiar.dg world.dg interface.dg markov.dg soliloquy.dg nudge.dg automap.dg actions.dg files = $(sourcefiles) stdlib.dg outname = familiar @@ -12,7 +15,7 @@ test: web $(outname).aastory: $(files) hints.html $(DIALOGC) -vv -t aa -o familiar.aastory $(files) -web: $(outname).aastory +web: $(outname).aastory $(AAMBUNDLE) rm -rf web $(AAMBUNDLE) -o web $(outname).aastory mv web/play.html web/index.html diff --git a/test/gosling/Makefile b/test/gosling/Makefile index 4f25e1c..6720655 100644 --- a/test/gosling/Makefile +++ b/test/gosling/Makefile @@ -1,36 +1,4 @@ # VERY IMPORTANT: This should be a version of gosling compiled with Dialog 1a/01 or earlier, ensuring that it uses version 0.x of the Å-machine # The purpose is to ensure the interpreters can still handle old story files -# Call `make DIFF=meld` to get a fancy diff -DIFF = diff - -DIR6502 = ../../src/6502 -AAMBOX = $(DIR6502)/aambox6502 -FRONTEND = $(DIR6502)/aambox_frontend.bin - -$(AAMBOX): - $(MAKE) -C $(DIR6502) aambox6502 - -$(FRONTEND): - $(MAKE) -C $(DIR6502) aambox_frontend.bin - -all: test - -test: test.js test.6502 - -test.js: gosling.js.out - $(DIFF) gosling.js.out gosling.js.gold - -gosling.js.out: gosling.aastory - node ../../src/js/nodefrontend.js -s 1234 gosling.aastory gosling.js.out - -test.6502: gosling.6502.out - $(DIFF) gosling.6502.out gosling.6502.gold - -gosling.6502.out: gosling.aastory $(AAMBOX) $(FRONTEND) - $(AAMBOX) -s 1234 $(FRONTEND) gosling.aastory gosling.6502.out - -clean: - rm -f *.out - -.PHONY: all test test.js test.6502 clean +include ../common.mk \ No newline at end of file diff --git a/test/impossible/Makefile b/test/impossible/Makefile index 7050d40..fb2676f 100644 --- a/test/impossible/Makefile +++ b/test/impossible/Makefile @@ -1,33 +1,4 @@ -# Call `make DIFF=meld` to get a fancy diff -DIFF = diff +# story name is inferred from name of directory +# so all we need to do is include -DIR6502 = ../../src/6502 -AAMBOX = $(DIR6502)/aambox6502 -FRONTEND = $(DIR6502)/aambox_frontend.bin - -$(AAMBOX): - $(MAKE) -C $(DIR6502) aambox6502 - -$(FRONTEND): - $(MAKE) -C $(DIR6502) aambox_frontend.bin - -all: test - -test: test.js test.6502 - -test.js: impossible.js.out - $(DIFF) impossible.js.out impossible.js.gold - -impossible.js.out: impossible.aastory - node ../../src/js/nodefrontend.js -s 1234 impossible.aastory impossible.js.out - -test.6502: impossible.6502.out - $(DIFF) impossible.6502.out impossible.6502.gold - -impossible.6502.out: impossible.aastory $(AAMBOX) $(FRONTEND) - $(AAMBOX) -s 1234 $(FRONTEND) impossible.aastory impossible.6502.out - -clean: - rm -f *.out - -.PHONY: all test test.js test.6502 clean +include ../common.mk \ No newline at end of file From 02a173fbb023eb1e38ac3864a8b1a9387a481e17 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 31 Aug 2026 13:19:57 +0200 Subject: [PATCH 2/2] makefiles: fallback rule so "make --directory=./src aambundle" works from Github CI --- src/Makefile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Makefile b/src/Makefile index 2da6c26..3838221 100644 --- a/src/Makefile +++ b/src/Makefile @@ -32,6 +32,7 @@ tidy: clean: tidy rm -f aamshow aambundle aamrun aamshow.exe aambundle.exe aamrun.exe + rm -f mkheader mkfontdata table_*.h tables_*.h $(MAKE) -C 6502 clean install: all @@ -102,9 +103,20 @@ aambundle.exe: aambundle.c bundle_web.c bundle_c64.c bundle_apple2.c \ tables_6502font.h ${MINGW32} ${CFLAGS} -o $@ aambundle.c bundle_web.c bundle_c64.c bundle_apple2.c +# Fall back rules when you make a blob from here +# e.g. "make -C src aambundle" redirects to "make -C 6502" +BLOBS = 6502/a2.system 6502/a2_0boot.bin 6502/a2_sboot.bin \ + 6502/c64_drivecode.bin 6502/c64_loader.prg 6502/c64_crunched.bin + +$(BLOBS): + $(MAKE) -C 6502 $(@F) + mkheader: mkheader.c ${CC} ${CFLAGS} -o $@ mkheader.c +mkfontdata: mkfontdata.c + ${CC} ${CFLAGS} -o $@ mkfontdata.c + tables_6502font.h: 6502/fontdef.txt mkfontdata ./mkfontdata <6502/fontdef.txt >tables_6502font.h