-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
469 lines (404 loc) · 16.3 KB
/
Copy pathMakefile
File metadata and controls
469 lines (404 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# Granny Smith — Top-level Build System
#
# Targets:
# all (default) Build WASM emulator (release mode)
# debug Build WASM emulator (debug mode)
# sanitize Build WASM emulator (sanitizer mode)
# headless Build native headless CLI
# unit-test Build and run all unit tests
# integration-test Build headless and run integration tests
# integration-test-valgrind Run integration tests under Valgrind
# e2e-test Run Playwright end-to-end tests
# test Run unit + integration tests
# run Build and serve the UI on :8080 (or the next free port)
# clean Remove all build artifacts (wasm, headless,
# unit, integration, e2e)
# help Show available targets
# -- Emscripten compiler + version guard --
CC ?= emcc
ifneq ($(notdir $(CC)),emcc)
ifneq (,$(shell command -v emcc 2>/dev/null))
override CC := emcc
endif
endif
EMSDK_REQUIRED_VERSION := 4.0.10
# Targets that do not require the Emscripten toolchain
NON_EMCC_TARGETS := clean help headless unit-test \
integration-test integration-test-valgrind e2e-test test
# Only validate emcc when a WASM build target is requested
ifeq (,$(filter $(NON_EMCC_TARGETS),$(MAKECMDGOALS)))
ifeq (,$(shell command -v $(CC) 2>/dev/null))
$(error emcc not found. Run scripts/setup_emsdk.sh $(EMSDK_REQUIRED_VERSION))
endif
EMCC_VERSION_LINE := $(shell $(CC) --version 2>/dev/null | head -n1)
EMCC_VERSION := $(shell echo "$(EMCC_VERSION_LINE)" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
ifeq ($(strip $(EMCC_VERSION_LINE)),)
$(error Unable to execute $(CC); ensure Emscripten environment is loaded)
endif
ifeq ($(findstring emcc,$(EMCC_VERSION_LINE)),)
$(error Compiler is '$(EMCC_VERSION_LINE)'. Expected emcc.)
endif
ifneq ($(EMCC_VERSION),$(EMSDK_REQUIRED_VERSION))
$(warning emcc version $(EMCC_VERSION) != required $(EMSDK_REQUIRED_VERSION))
endif
endif
# -- Directories --
BUILD_DIR := build
OBJ_DIR := $(BUILD_DIR)/wasm
WEB2_DIR := app/web2
WEB2_DIST := $(WEB2_DIR)/dist
CORE_DIR := src/core
MACHINES_DIR := src/machines
PLATFORM_DIR := src/platform/wasm
PEELER_DIR := src/peeler
# -- Source discovery --
# Wildcard patterns auto-discover new .c files in each subdirectory.
# Core emulator sources (platform-agnostic)
CORE_SRC := $(wildcard $(CORE_DIR)/*.c) \
$(wildcard $(CORE_DIR)/cpu/*.c) \
$(wildcard $(CORE_DIR)/cpu/dsp3210/*.c) \
$(wildcard $(CORE_DIR)/cpu/ppc/*.c) \
$(wildcard $(CORE_DIR)/memory/*.c) \
$(wildcard $(CORE_DIR)/peripherals/*.c) \
$(wildcard $(CORE_DIR)/peripherals/nubus/*.c) \
$(wildcard $(CORE_DIR)/peripherals/nubus/cards/*.c) \
$(wildcard $(CORE_DIR)/peripherals/pci/*.c) \
$(wildcard $(CORE_DIR)/peripherals/pci/cards/*.c) \
$(wildcard $(CORE_DIR)/scheduler/*.c) \
$(wildcard $(CORE_DIR)/debug/*.c) \
$(wildcard $(CORE_DIR)/storage/*.c) \
$(wildcard $(CORE_DIR)/network/*.c) \
$(wildcard $(CORE_DIR)/shell/*.c) \
$(wildcard $(CORE_DIR)/object/*.c) \
$(wildcard $(CORE_DIR)/vfs/*.c) \
$(shell find $(MACHINES_DIR) -name '*.c')
# Platform-specific sources (WASM/Emscripten)
PLATFORM_SRC := $(wildcard $(PLATFORM_DIR)/*.c)
# Peeler library sources
PEELER_SRC := $(PEELER_DIR)/lib/peeler.c \
$(PEELER_DIR)/lib/err.c \
$(PEELER_DIR)/lib/util.c \
$(PEELER_DIR)/lib/formats/bin.c \
$(PEELER_DIR)/lib/formats/cpt.c \
$(PEELER_DIR)/lib/formats/hqx.c \
$(PEELER_DIR)/lib/formats/sit.c \
$(PEELER_DIR)/lib/formats/sit3.c \
$(PEELER_DIR)/lib/formats/sit13.c \
$(PEELER_DIR)/lib/formats/sit15.c
SRC := $(CORE_SRC) $(PLATFORM_SRC) $(PEELER_SRC)
# Object and dependency files (mirror source tree under OBJ_DIR)
OBJ := $(patsubst %.c,$(OBJ_DIR)/%.o,$(SRC))
DEP := $(OBJ:.o=.d)
OUTPUT := $(BUILD_DIR)/main.mjs
# -- GS declaration-ROM 68K fragments --
# Assembled by m68k binutils into build/vrom68k/ (shared with the
# headless build — the fragments are target-independent data). Defines
# VROM68K_HEADER and the rules that produce it; gsvrom_data.c includes
# the generated header.
include src/core/peripherals/nubus/vrom68k/vrom68k.mk
# -- Build mode (release | debug | sanitize) --
MODE ?= release
ifeq ($(MODE),debug)
# -Og avoids excessive WASM locals that can exceed browser limits.
# -DGS_DEBUG enables thread-affinity assertions (see worker_thread.h)
# and any other invariants gated behind that flag.
MODE_CFLAGS := -Og -g -DGS_DEBUG
else ifeq ($(MODE),sanitize)
MODE_CFLAGS := -O1 -g -DGS_DEBUG -fsanitize=address,undefined -sSTACK_OVERFLOW_CHECK=2
else
# Production profile: GS_ASSERT / scheduler invariant walks / libc assert
# compiled out (perf proposal P4) — measured +4.5% native, more in wasm
# (the invariant walks run per RAF frame-unit and per event insert).
# Debug/sanitize builds keep them all.
MODE_CFLAGS := -O2 -DGS_FAST -DNDEBUG
endif
# -- Include paths --
PEELER_INCLUDES := -I$(PEELER_DIR)/include -I$(PEELER_DIR)/lib
INCLUDES := -I$(CORE_DIR) \
-I$(CORE_DIR)/cpu \
-I$(CORE_DIR)/cpu/dsp3210 \
-I$(CORE_DIR)/cpu/ppc \
-I$(CORE_DIR)/memory \
-I$(CORE_DIR)/peripherals \
-I$(CORE_DIR)/peripherals/nubus \
-I$(CORE_DIR)/peripherals/nubus/cards \
-I$(CORE_DIR)/peripherals/pci \
-I$(CORE_DIR)/peripherals/pci/cards \
-I$(CORE_DIR)/scheduler \
-I$(CORE_DIR)/debug \
-I$(CORE_DIR)/storage \
-I$(CORE_DIR)/network \
-I$(CORE_DIR)/shell \
-I$(CORE_DIR)/object \
-I$(CORE_DIR)/vfs \
-I$(MACHINES_DIR) \
-I$(MACHINES_DIR)/runtime \
-I$(MACHINES_DIR)/mac030 \
-I$(MACHINES_DIR)/glue \
-I$(MACHINES_DIR)/mdu \
-I$(MACHINES_DIR)/mcu \
-I$(MACHINES_DIR)/av \
-I$(MACHINES_DIR)/pdm \
-I$(MACHINES_DIR)/tnt \
-I$(MACHINES_DIR)/oss \
-I$(MACHINES_DIR)/compact \
-I$(MACHINES_DIR)/lisa \
-I$(PLATFORM_DIR) \
-I$(VROM68K_OUT)
# -- Compile flags (source -> object) --
# -MMD -MP generates .d dependency files alongside each .o so that
# header changes trigger the correct recompilations.
CFLAGS := -MMD -MP $(MODE_CFLAGS) \
-pthread \
$(PEELER_INCLUDES) $(INCLUDES) $(EXTRA_CFLAGS)
# -- Link flags (objects -> final binary) --
LDFLAGS := $(MODE_CFLAGS) \
-s MODULARIZE=1 \
-s EXPORT_NAME="createModule" \
-sWASMFS \
-sFORCE_FILESYSTEM \
-lopfs.js \
-pthread \
-sPROXY_TO_PTHREAD \
-sOFFSCREENCANVAS_SUPPORT \
-sOFFSCREEN_FRAMEBUFFER \
-sOFFSCREENCANVASES_TO_PTHREAD='\#screen' \
-s EXPORTED_RUNTIME_METHODS=['FS','cwrap','ccall','stringToUTF8','UTF8ToString','HEAP16','HEAP32','HEAPU8','wasmMemory'] \
-s EXPORTED_FUNCTIONS="['_main','_get_js_bridge']" \
-s STACK_SIZE=5MB \
-s ALLOW_MEMORY_GROWTH=1 \
-s USE_WEBGL2=1 \
$(EXTRA_CFLAGS) $(EXTRA_LDFLAGS)
# -- Phony targets --
.PHONY: all release debug sanitize run \
headless unit-test integration-test integration-test-valgrind \
e2e-test test clean help FORCE \
ui2 ui2-dev ui2-test ui2-check ui2-check-dist ui2-prod-smoke ui2-e2e ui2-diag run2
# -- WASM build --
all: $(OUTPUT)
release:
$(MAKE) MODE=release all
debug:
$(MAKE) MODE=debug all
sanitize:
$(MAKE) MODE=sanitize all
# Compile each .c -> .o with automatic header dependency generation
$(OBJ_DIR)/%.o: %.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# Force-rebuild build_id.o so __DATE__/__TIME__ stay current
FORCE:
$(OBJ_DIR)/$(CORE_DIR)/build_id.o: FORCE
# gsvrom_data.c embeds the generated fragments header.
$(OBJ_DIR)/$(CORE_DIR)/peripherals/nubus/gsvrom_data.o: $(VROM68K_HEADER)
# Link all objects into the final WASM module
$(OUTPUT): $(OBJ)
@mkdir -p $(dir $@)
@echo "Linking ($(MODE)) with $(CC)"
$(CC) $(LDFLAGS) $(OBJ) -o $@
# Include auto-generated header dependency files
-include $(DEP)
# -- Run --
# Optional boot-media variables (paths relative to repo root):
# ROM=path/to/rom.bin VROM=path/to/vrom.bin
# FD0=path/to/floppy.img FD1=path/to/floppy2.img
# HD0=path/to/hd.zip HD1=... (up to HD7)
# SPEED=max|realtime|hardware
# Build the URL query string from media variables.
RUN_PARAMS :=
ifdef ROM
RUN_PARAMS += rom=/$(ROM)
endif
ifdef VROM
RUN_PARAMS += vrom=/$(VROM)
endif
ifdef FD0
RUN_PARAMS += fd0=/$(FD0)
endif
ifdef FD1
RUN_PARAMS += fd1=/$(FD1)
endif
ifdef HD0
RUN_PARAMS += hd0=/$(HD0)
endif
ifdef HD1
RUN_PARAMS += hd1=/$(HD1)
endif
ifdef HD2
RUN_PARAMS += hd2=/$(HD2)
endif
ifdef HD3
RUN_PARAMS += hd3=/$(HD3)
endif
ifdef HD4
RUN_PARAMS += hd4=/$(HD4)
endif
ifdef HD5
RUN_PARAMS += hd5=/$(HD5)
endif
ifdef HD6
RUN_PARAMS += hd6=/$(HD6)
endif
ifdef HD7
RUN_PARAMS += hd7=/$(HD7)
endif
ifdef SPEED
RUN_PARAMS += speed=$(SPEED)
endif
# Join params list with & to form query string.
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
RUN_QS = $(subst $(SPACE),&,$(strip $(RUN_PARAMS)))
# Enable fallback root when any media variable is specified.
RUN_SERVER_FLAGS :=
ifneq ($(strip $(RUN_PARAMS)),)
RUN_SERVER_FLAGS += --fallback-root .
endif
# `make run` serves the web2 UI (app/web2/dist/). Depends on `all` so the
# WASM build runs first; ui2 then copies the fresh build artifacts into the
# dist directory.
# The port is a preference: if 8080 is taken (another checkout's `make run`
# in another editor), the server moves to the next free one and prints the
# URL it actually took. Override with RUN_PORT=8090.
RUN_PORT ?= 8080
run: all ui2
ifneq ($(strip $(RUN_PARAMS)),)
python3 scripts/dev_server.py --root $(WEB2_DIST) --port $(RUN_PORT) $(RUN_SERVER_FLAGS) --default-params '$(RUN_QS)'
else
python3 scripts/dev_server.py --root $(WEB2_DIST) --port $(RUN_PORT)
endif
# -- Headless native build --
headless:
$(MAKE) -f Makefile.headless
# -- Test targets --
# Build and run all unit tests
unit-test:
$(MAKE) -C tests/unit run
# Build headless and run all integration tests
integration-test:
$(MAKE) -C tests/integration test
# Run a single integration test by name
integration-test-%:
$(MAKE) -C tests/integration test-$*
# Run integration tests under Valgrind memcheck
integration-test-valgrind:
$(MAKE) -C tests/integration test-valgrind
# Run the web2 (Svelte) Playwright end-to-end tests. Alias for ui2-e2e.
e2e-test: ui2-e2e
# Run unit + integration tests
test: unit-test integration-test
# -- UI (app/web2) — Svelte 5 + Vite + TypeScript --
# `make run` builds and serves this UI.
ui2:
cd $(WEB2_DIR) && npm ci --silent && npm run build
@# Phase 3: copy WASM build artifacts into the served dist directory.
@# Skipped silently if the WASM build hasn't run yet — `make` produces them.
@if [ -f $(BUILD_DIR)/main.mjs ]; then \
cp $(BUILD_DIR)/main.mjs $(BUILD_DIR)/main.wasm $(WEB2_DIST)/ ; \
if [ -f $(BUILD_DIR)/coi-serviceworker.js ]; then \
cp $(BUILD_DIR)/coi-serviceworker.js $(WEB2_DIST)/ ; \
fi ; \
if [ -d $(BUILD_DIR)/wasm ]; then \
cp -R $(BUILD_DIR)/wasm $(WEB2_DIST)/wasm ; \
fi ; \
else \
echo "Note: $(BUILD_DIR)/main.mjs not found; run 'make' first to produce WASM artifacts" ; \
fi
ui2-dev:
cd $(WEB2_DIR) && npm run dev
ui2-test:
cd $(WEB2_DIR) && npm test
ui2-check:
cd $(WEB2_DIR) && npm run check && npm run lint
# Static validation of app/web2/dist/ produced by `make ui2` (or a
# bare `npm run build`). Catches origin-rooted URLs that 404 under
# subpath deploys (the v0.4.0–v0.4.2 deploy-blocker class) and
# verifies the COI service worker is wired up. Cheap — pure file
# inspection, no browser.
ui2-check-dist:
@/usr/bin/env node scripts/check-dist.mjs
# Playwright smoke against the production-built bundle, served from
# a subpath WITHOUT pre-set COOP/COEP headers. Forces the bundle to
# bootstrap COI through its own service worker — the exact
# environment GitHub Pages provides. Requires Playwright + chromium
# already installed via tests/e2e/ (`cd tests/e2e && npm ci &&
# npx playwright install chromium`). The webServer block in the
# config starts scripts/prod-smoke-server.mjs automatically.
ui2-prod-smoke: ui2 ui2-check-dist
cd tests/e2e && npx playwright test --config=playwright.prod-smoke.config.ts
# Playwright end-to-end test for the web2 (Svelte) Filesystem tab. Drives the
# real UI — descend a disk image, multi-select, copy out, delete, copy again —
# against the served dist; the webServer block builds (`make ui2`) and serves
# app/web2/dist with the COOP/COEP headers the worker needs. No machine boot;
# the only synthesised step is the HTML5 drag gesture (Playwright/CDP can't
# drive native DnD). Requires Playwright + chromium installed under tests/e2e.
ui2-e2e:
cd tests/e2e && npx playwright test --config=playwright.web2.config.ts
# Headless diagnostic — spawns the dev server + drives Chromium via
# Playwright, captures console output / pageerror / xterm contents,
# and prints a JSON report. Useful for triaging "doesn't boot" bugs
# without an interactive browser. Depends on tests/e2e's Playwright
# install (chromium pre-fetched there).
#
# Environment knobs (forwarded to scripts/ui2-diag.mjs):
# GL=swiftshader (default) | native | disable — WebGL backend
# COMMANDS='cpu.pc;rom list' — type these in
# SETTLE=8000 — wait ms after boot
# PORT=18181 — dev server port
# HEADLESS=0 — show the browser
ui2-diag: ui2
@/usr/bin/env node scripts/ui2-diag.mjs
# run2 is an alias for `run` for muscle-memory continuity. The Phase 7
# retire pass made `run` itself serve the new UI; this alias can be
# dropped in a future cleanup.
run2: run
# -- Clean (everything) --
# Removes all build artifacts: wasm, headless, unit, integration, e2e
clean:
rm -rf $(BUILD_DIR)
$(MAKE) -C tests/unit clean
rm -rf tests/integration/test-results
rm -rf tests/e2e/test-results
# -- Help --
help:
@echo "Granny Smith Build System"
@echo ""
@echo "Build targets:"
@echo " all (default) Build WASM emulator (release)"
@echo " debug Build WASM emulator (debug)"
@echo " sanitize Build WASM emulator (sanitizers)"
@echo " headless Build native headless CLI"
@echo " run Build the UI and serve on :8080 (next free port if taken; RUN_PORT=n)"
@echo ""
@echo "UI targets (app/web2 — Svelte 5 + Vite + TS):"
@echo " ui2 Build the Svelte UI (production)"
@echo " ui2-dev Start Vite dev server (HMR) on :5173"
@echo " ui2-check Run svelte-check + ESLint + Prettier"
@echo " ui2-test Run Vitest"
@echo " ui2-e2e Run the web2 Playwright e2e suite"
@echo " run2 Alias for run (kept for muscle-memory)"
@echo ""
@echo "Test targets:"
@echo " test Run unit + integration tests"
@echo " unit-test Build and run all unit tests"
@echo " integration-test Build headless; run integration tests"
@echo " integration-test-<name> Run single integration test (e.g. se30-format-hd)"
@echo " integration-test-valgrind Integration tests under Valgrind"
@echo " e2e-test Run the web2 Playwright e2e suite (alias for ui2-e2e)"
@echo ""
@echo "Maintenance:"
@echo " clean Remove all build artifacts"
@echo " help Show this message"
@echo ""
@echo "Options:"
@echo " MODE=release|debug|sanitize Build mode (default: release)"
@echo " EXTRA_CFLAGS=... Additional compiler flags"
@echo ""
@echo "Boot media (for 'run' target):"
@echo " ROM=path/to/rom.bin ROM image"
@echo " FD0=path/to/floppy.img Floppy disk image"
@echo " HD0=path/to/hd.zip ...HD7 Hard disk images (zip or raw)"
@echo " SPEED=max|realtime|hardware Emulation speed"
@echo ""
@echo "Example:"
@echo " make run ROM=tests/data/roms/plus-v3-4d1f8172.rom HD0=tests/data/systems/hd.zip"