BUILD / Build System
Build System
Make targets, compiler flags, platform prerequisites, and WebAssembly build flow.
Build System
Makefile Overview
The project uses a GNU Makefile with explicit per-directory wildcards. New .c files in recognized source directories are compiled automatically; new source directories need matching SRCS and pattern-rule entries.
CC ?= clang
CFLAGS = -std=c11 -Wall -Wextra -Wpedantic $(shell sdl2-config --cflags)
LIBS = $(shell sdl2-config --libs) -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lm
OUTDIR = out
OBJDIR = $(OUTDIR)/obj
TARGET = $(OUTDIR)/super-mango
SRCDIR = src
SRCS = $(wildcard $(SRCDIR)/*.c) \
$(wildcard $(SRCDIR)/collectibles/*.c) \
$(wildcard $(SRCDIR)/collision/*.c) \
$(wildcard $(SRCDIR)/core/*.c) \
$(wildcard $(SRCDIR)/effects/*.c) \
$(wildcard $(SRCDIR)/entities/*.c) \
$(wildcard $(SRCDIR)/hazards/*.c) \
$(wildcard $(SRCDIR)/input/*.c) \
$(wildcard $(SRCDIR)/levels/*.c) \
$(wildcard $(SRCDIR)/player/*.c) \
$(wildcard $(SRCDIR)/render/*.c) \
$(wildcard $(SRCDIR)/screens/*.c) \
$(wildcard $(SRCDIR)/surfaces/*.c) \
$(SRCDIR)/editor/serializer.c \
$(SRCDIR)/editor/serializer_emit.c \
$(SRCDIR)/editor/serializer_io.c \
$(SRCDIR)/editor/serializer_load.c \
$(SRCDIR)/editor/serializer_load_climbables.c \
$(SRCDIR)/editor/serializer_load_collectibles.c \
$(SRCDIR)/editor/serializer_load_config.c \
$(SRCDIR)/editor/serializer_load_enemies.c \
$(SRCDIR)/editor/serializer_load_geometry.c \
$(SRCDIR)/editor/serializer_load_hazards.c \
$(SRCDIR)/editor/serializer_load_header.c \
$(SRCDIR)/editor/serializer_load_layers.c \
$(SRCDIR)/editor/serializer_load_surfaces.c \
$(SRCDIR)/editor/serializer_parse.c \
$(SRCDIR)/editor/serializer_save.c \
$(SRCDIR)/editor/serializer_types.c \
vendor/tomlc17/tomlc17.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
DEPS = $(OBJS:.o=.d)
Key Variables
| Variable | Value | Description |
|---|---|---|
CC | clang intended; pass explicitly for parity | C compiler. Use CC=clang for CI/local parity; GNU Make may otherwise use its built-in CC=cc. |
CFLAGS | see below | Compiler flags |
LIBS | see below | Linker flags |
TARGET | out/super-mango | Output binary path |
SRCS | explicit per-directory wildcards plus editor serializer files | Game C sources from recognized source directories, TOML serializer modules, and tomlc17 |
OBJDIR | out/obj | Object/dependency root that mirrors source paths |
OBJS | $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) | Object files under out/obj/... |
DEPS | $(OBJS:.o=.d) | Auto-generated dependency files beside object files under out/obj/... |
Compiler Selection Caveat
For reproducible local results, pass CC=clang explicitly:
make test CC=clang
make smoke CC=clang
The Makefile uses CC ?= clang, but GNU Make defines a built-in CC=cc, so some local invocations resolve to cc unless the compiler is overridden on the command line.
Compiler Flags Explained
| Flag | Meaning |
|---|---|
-std=c11 | Compile as C11 (ISO/IEC 9899:2011) |
-Wall | Enable common warnings |
-Wextra | Enable extra warnings beyond -Wall |
-Wpedantic | Strict ISO compliance warnings |
-MMD | Generate .d dependency files for each .o (tracks header changes) — passed in compile rule, not in CFLAGS |
-MP | Add phony targets for each dependency (prevents errors when headers are deleted) — passed in compile rule, not in CFLAGS |
$(shell sdl2-config --cflags) | SDL2 include paths (-I/opt/homebrew/include/SDL2) |
Linker Flags Explained
| Flag | Meaning |
|---|---|
$(shell sdl2-config --libs) | SDL2 core library (-L/opt/homebrew/lib -lSDL2) |
-lSDL2_image | PNG/JPG texture loading |
-lSDL2_ttf | TrueType font rendering |
-lSDL2_mixer | Audio mixing (WAV, MP3, OGG) |
-lm | Math library (math.h functions: sinf, cosf, fmodf, etc.) |
Build Targets
make / make all
Compiles game source files from the Makefile’s source directory list to .o objects, then links them into out/super-mango.
make
Steps:
- Creates
out/directory if it does not exist - Compiles each listed source file →
.o - Links all
.ofiles →out/super-mango - On macOS (
uname -s == Darwin), ad-hoc code signs the binary withcodesign --force --sign - $@(required on Apple Silicon to avoidKilled: 9errors). On other platforms this step is skipped
make run
Builds (if out of date) then immediately executes the binary (no CLI flags).
make run
The binary must be run from the repo root because asset paths are relative:
IMG_LoadTexture(renderer, "assets/sprites/backgrounds/sky_blue.png");
Mix_LoadWAV("assets/sounds/player/player_jump.wav");
make run-debug
Builds (if out of date) then runs the binary with the --debug flag, which enables the debug overlay: FPS counter, collision hitbox visualization, and scrolling event log.
make run-debug
make run-level LEVEL=path
Builds (if out of date) then runs the binary with the --level flag, loading a specific TOML level file.
make run-level LEVEL=levels/00_sandbox_01.toml
make run-level-debug LEVEL=path
Builds (if out of date) then runs the binary with both --debug and --level flags, loading a specific TOML level file with the debug overlay enabled.
make run-level-debug LEVEL=levels/00_sandbox_01.toml
make editor
Compiles the standalone level editor into out/super-mango-editor. The editor is a separate binary with its own source files in src/editor/ and the tomlc17 TOML parser from vendor/tomlc17/.
make editor
make run-editor
Builds the editor (if out of date) then immediately runs it.
make run-editor
make web
Compiles the game to WebAssembly using the Emscripten SDK (emcc). Requires Emscripten to be installed and emcc on PATH.
make web
Produces out/super-mango.html, .js, .wasm, and .data (bundled assets/sounds). SDL2 ports are compiled from source by Emscripten on first build; subsequent builds reuse cached port libraries. Uses a custom shell template from web/shell.html.
The target also produces debug boot artifacts (out/super-mango-debug.html and companions) for direct debug HTML launches. The docs-site browser debug button uses the normal super-mango.js payload and passes --debug at boot.
For release confidence, the GitHub Actions WebAssembly build is authoritative. Some local/container Emscripten installs can fail before reaching Super Mango code when Emscripten 3.1.58 builds SDL_ttf’s HarfBuzz port with newer Clang warnings (-Wnontrivial-memcall). If that host-toolchain issue appears locally, treat the CI make web + artifact smoke from build.yml and the Pages assembly smoke from deploy.yml as the trusted WASM verification path.
make test
Builds and runs native regression harnesses for pure logic that does not require opening an SDL window.
make test
Current test binaries (14):
out/level-serializer-testout/level-validate-testout/runtime-load-testout/rail-testout/entity-utils-testout/collision-testout/phase-transition-testout/exporter-testout/editor-validation-testout/gameplay-damage-testout/gameplay-config-testout/gameplay-score-testout/game-overlay-testout/game-events-test
make validate-levels
Runs the Python TOML validator against every levels/*.toml file. It checks parsing, referenced asset paths, next_phase links, and array counts against the C MAX_* constants.
make validate-levels
make smoke
Builds the game and editor, then runs every levels/*.toml with dummy SDL video/audio drivers for a bounded number of frames. It also runs the editor’s headless smoke mode.
make smoke SMOKE_FRAMES=5 SMOKE_SEED=1
make scripted-smoke
Builds the game and editor, then runs tools/run_scripted_smoke.py against every levels/*.toml for each seed in SMOKE_SEEDS. The runner writes deterministic replay scripts under out/replays-smoke/ and passes sanitized replay names to the game with --replay-script, injecting SDL key events such as move-right, jump-right, and pause/resume while still using dummy SDL video/audio in CI.
make scripted-smoke SMOKE_FRAMES=5 SMOKE_SEEDS="1 7 23"
make sanitize
Runs make test in a separate out-sanitize/ tree with AddressSanitizer and UndefinedBehaviorSanitizer enabled.
make sanitize
make sanitize-smoke
Builds sanitizer-instrumented native game/editor binaries in out-sanitize/, then runs the dummy SDL smoke pass against every TOML level plus the editor smoke mode. This complements make sanitize by covering SDL/resource startup paths, not just pure logic harnesses.
make sanitize-smoke SMOKE_FRAMES=5 SMOKE_SEED=1
make level-catalog
Regenerates docs/wiki/level-catalog.md from levels/*.toml using tools/generate_level_catalog.py. Use this after adding levels or changing level metadata/content counts.
make level-catalog
make docs-drift
Runs the generated level-catalog freshness check, generated overlay-snapshot freshness check, tools/check_docs_drift.py, and tools/check_roadmap_quality.py. Together they compare Makefile test targets, README/agent/docs command summaries, source-file map entries, layer TOML snippets, workflow docs, runtime flags, key constants, level prose counts, TOML line endings, overlay text snapshots, local Emscripten caveats, and scripted-smoke wiring against the current repository.
make docs-drift
make clean
Removes all build artifacts.
make clean
Deletes legacy in-source .o / .d files from recognized source directories and removes out/.
Prerequisites
macOS (Apple Silicon / Intel)
# Install Homebrew if needed: https://brew.sh
brew install sdl2 sdl2_image sdl2_ttf sdl2_mixer
# Xcode Command Line Tools (provides clang and make)
xcode-select --install
SDL2 libraries are installed to /opt/homebrew/ on Apple Silicon. sdl2-config resolves the correct paths automatically.
Linux — Debian / Ubuntu
sudo apt update
sudo apt install build-essential clang \
libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev
Linux — Fedora / RHEL / CentOS
sudo dnf install clang make \
SDL2-devel SDL2_image-devel SDL2_ttf-devel SDL2_mixer-devel
Linux — Arch Linux
sudo pacman -S clang make sdl2 sdl2_image sdl2_ttf sdl2_mixer
Windows (MSYS2)
- Install MSYS2
- Open the MSYS2 UCRT64 terminal:
pacman -S mingw-w64-ucrt-x86_64-clang \
mingw-w64-ucrt-x86_64-make \
mingw-w64-ucrt-x86_64-SDL2 \
mingw-w64-ucrt-x86_64-SDL2_image \
mingw-w64-ucrt-x86_64-SDL2_ttf \
mingw-w64-ucrt-x86_64-SDL2_mixer
- Build:
cd /c/path/to/super-mango-editor
make
- SDL2 DLLs must be in the same directory as the binary. Copy them from the MSYS2 prefix.
CI/CD Pipelines
Four GitHub Actions workflows handle automated builds and docs checks:
| Workflow | File | Trigger | Purpose |
|---|---|---|---|
| Build & Release | build.yml | Push to main, pull requests, v* tags, manual | Multi-platform native build, make test, make validate-levels, make editor, native game/editor smoke, Linux scripted smoke, WebAssembly build, WebAssembly artifact/package smoke; GitHub Release creation is limited to v* tags and manual dispatches |
| Docs | docs.yml | Docs pull requests, manual | make docs-drift, bun install --frozen-lockfile, bun run lint, bun run build under docs/ |
| CodeQL | codeql.yml | Push/PR to main, weekly | Automated code security and quality analysis |
| Deploy | deploy.yml | Successful main Build & Release workflow | Builds docs, copies WebAssembly artifacts, and deploys docs/out/ to GitHub Pages |
Native smoke uses dummy SDL drivers where supported: ./out/super-mango --level levels/00_sandbox_01.toml --smoke-test-frames 5 and ./out/super-mango-editor --smoke-test. WebAssembly smoke asserts out/super-mango.html, .js, .wasm, and .data exist.
Adding New Source Files
Because the Makefile uses per-subdirectory wildcards, any new .c file placed in src/ or a recognized source subdirectory is compiled automatically on the next make invocation. New source directories require adding a wildcard, compile rule, and clean entry.
# Example: adding an entity in a subdirectory
touch src/entities/new_enemy.c src/entities/new_enemy.h
make # new_enemy.c is compiled automatically
See Developer Guide for the full new-entity workflow.
Output Structure
After a successful build:
out/
├── super-mango ← the game binary
├── super-mango-editor ← the editor binary (make editor)
└── obj/
├── src/ ← game/editor objects mirror source paths
│ ├── core/*.o / *.d
│ ├── editor/*.o / *.d
│ ├── player/*.o / *.d
│ └── ...
└── vendor/tomlc17/tomlc17.o / .d