6 · Build & tooling
This chapter walks through every script and workflow that builds, tests, and ships Amalgame.
The compiler and its snapshot
Amalgame keeps the self-hosted compiler in tree, plus a portable known-good snapshot:
./amc— self-hosted, written in Amalgame. The everyday compiler. Source insrc/, output of./build_amc.sh../snapshot/amc— last known-goodamccaptured bytools/save-snapshot.sh. The portablesnapshot/amc_lib.cis committed; onegccinvocation rebuilds the binary on any platform (seesnapshot/INFO.md). This is the cold-start entry point and the recovery rung when./amcis broken mid-development.
Cold-start bootstrap
From a clean clone:
gcc -O2 -Iruntime snapshot/amc_lib.c -lgc -lm -o snapshot/amc
./build_amc.sh
Dependencies (Linux):
- gcc
- libgc-dev (Boehm GC — the only runtime dep since v0.8.31; HTTP
moved to the
amalgame-net-httpexternal package, dropping the libcurl link)
macOS uses brew install bdw-gc; Windows uses MSYS2 MinGW64
(mingw-w64-x86_64-{gcc,gc}). The same snapshot/amc_lib.c is the
cross-platform entry point everywhere.
./build_amc.sh — self-host build
The five-second loop:
Step 1 ./amc src/lexer/*.am src/parser/*.am … src/generator/gen_test.am -o gen_test
gcc -O2 -Iruntime gen_test.c -o gen_test
Step 2 ./gen_test # generates src/amc_lib.c (and inspection bundles)
Step 3 gcc -Iruntime src/amc_lib.c -lgc -lm -o amc
Notes:
- Step 1 uses
./amcif it exists, otherwise./snapshot/amc. From a clean clone, build./snapshot/amcfirst via the cold-start command above. - Step 1 tolerates non-zero exit from
amcas long as the.coutput was produced. This is the recurring "I just added a builtin and the running amc doesn't know it yet" case — gcc remains the real correctness gate. main.amis intentionally excluded from the gen_test source list. It declares its ownProgram.Main(the CLI entry), which would clash withgen_test.am'sProgram.Mainin the bundled binary.main.amis only compiled intoamc_lib.cvia gen6 in step 2.- File order in
AMC_SOURCESmatters for the bootstrap CGen: classes must appear before their dependents (since pass-2 emits forward decls + bodies file-by-file).diagnostics.amis listed beforeresolver.amfor that reason —SourceMapandSourceSnippethave to be visible.
The build is hermetic — no environment variables, no global state.
Re-running ./build_amc.sh is always safe.
Tests — AM bundles via amc test
Depuis 2026-05-22 la suite de tests vit comme des bundles *_test.am
auto-découverts par amc test. Chaque bundle compile ses samples une
seule fois et grep-vérifie la stdout capturée pour chaque assertion —
~6× plus rapide que les anciens runners bash (stdlib : 3m34s → 8s).
Les runners bash legacy ont été supprimés le 2026-05-24 ; amc test
est désormais le seul chemin.
| Bundle | Cas | Couverture |
|---|---|---|
tests/fmt/fmt_test.am |
12 | Formatter idempotence + semantic preservation |
tests/amc_new/amc_new_test.am |
38 | amc new scaffolder smoke tests |
tests/stdlib_bundle/stdlib_test.am |
196 | IO/String/Collections/Json/Toml/Path/MsgPack/PR + 2 e2e |
tests/core_bundle/core_test.am |
332 | Core lang, namespace, interfaces, enums, lambda, LSP, DAP, LLM tooling |
| Total | 578 | + 5 SKIP (HTTP moved to amalgame-net-http) |
./amc test ./tests/ # tout (~42s) — auto-discovery
./amc test ./tests/core_bundle/ # une suite (29s)
./amc test ./tests/stdlib_bundle/ # stdlib uniquement (8s)
./tests/run_all_tests.sh # wrapper d'une ligne autour de `amc test ./tests/`
Flags
| Flag | Effet |
|---|---|
--filter <pat> |
N'exécute que les *_test.am dont le chemin contient <pat> (substring, pas une regex) |
--ci |
Sortie terse : drop [PASS]/[SKIP] et les en-têtes par fichier, garde [FAIL] + tally |
--list |
Imprime les chemins découverts (post-filter) et exit 0 — pas de compile, pas de run |
--help / -h |
Imprime ce tableau |
amc test prune les répertoires fixtures/ lors du crawl : les
fichiers fixture (LSP workspace, test-runner self-test) restent
accessibles via un chemin explicite (amc test ./tests/fixtures/<x>/)
mais ne sont pas auto-exécutés depuis la racine.
Continuous integration
.github/workflows/ci.yml — runs on every push and PR to main /
develop:
| Job | Runs on | What it does |
|---|---|---|
| linux | ubuntu-latest | apt deps · gcc snapshot/amc_lib.c · ./build_amc.sh · tests |
| macos | macos-latest (arm64) | brew deps · gcc src/amc_lib.c · smoke compile hello |
| windows | windows-latest (MSYS2) | pacman deps · gcc src/amc_lib.c · smoke compile hello |
All three platforms validate that the tracked amc_lib.c is
portable and produces a working binary.
Releases
.github/workflows/release.yml — runs on every push of a v* tag:
| Job | What it produces |
|---|---|
| build-linux | amc-X.Y.Z-linux-x86_64.tar.gz + .sha256 |
| build-macos | amc-X.Y.Z-macos-arm64.tar.gz + .sha256 |
| build-windows | amc-X.Y.Z-windows-x86_64.zip (DLLs bundled) + .sha256 |
| publish | aggregates checksums, creates a GitHub Release |
The Windows zip bundles the MinGW DLLs the binary actually links
against (libgc, libgcc_s_seh, libwinpthread, etc.) — users
install the zip and run amc.exe without any external dependency.
Trigger a release:
git tag v0.4.0
git push origin v0.4.0
# CI takes ~10 minutes, the release shows up on GitHub when done.
workflow_dispatch is also enabled for testing the workflow without
cutting a real release.
Inno Setup installer (Windows)
install/windows/amalgame.iss produces a .exe installer for
Windows users via Inno Setup 6+:
# Drop a portable MinGW64 (e.g. from winlibs.com) into install/windows/gcc-bundle/
iscc install/windows/amalgame.iss
# → Output/amalgame-X.Y.Z-setup.exe
The installer ships amc.exe, runtime/_runtime.h, the docs, and
the bundled MinGW64 toolchain so users get a working amc + gcc
out of the box.
Homebrew formula
install/homebrew/amalgame.rb — for brew tap distribution. Update
the version and SHA256 every release.
Local hygiene
git clean -fdxwill remove generated.cand binaries in the working tree. Be careful — it also removesgen_test,amc, andsrc/amc_lib.c. Use./build_amc.shafterwards to regenerate../CLEANUP.shis a legacy helper that removed older debug artefacts. It's mostly a no-op today.
Editor support
editors/vscode/ is a complete VS Code extension (TextMate grammar,
language configuration, LSP client, README). Install for development:
ln -s "$(pwd)/editors/vscode" ~/.vscode/extensions/amalgame-0.1.0
# Reload window: Ctrl+Shift+P → Developer: Reload Window
The extension spawns amc lsp on .am files via stdio JSON-RPC.
Point it at your local build by setting in your VS Code settings:
"amalgame.serverPath": "/abs/path/to/Amalgame/amc"
The LSP server (since v0.3.4 for diagnostics, v0.3.5 for hover + completion) currently provides:
- Diagnostics — resolver + typechecker errors published on
every
didOpen/didChange, with the offending token underlined. - Hover — Markdown tooltip showing the inferred type of the
identifier under the cursor (
name: type).nullwhen the cursor isn't on a typed expression. - Completion — global symbol list (built-in types,
functions, user classes / enums) with
CompletionItemKindhints. The.trigger is reserved for v2.5 member completion (obj.<cursor>narrowed to the receiver's type).
amc lsp runs in stdio mode by default. Manually drive it for
debugging via:
echo -e 'Content-Length: 41\r\n\r\n{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | ./amc lsp
Debugging .am programs (amc dap, since v0.8.0)
amc dap is a Debug Adapter Protocol server. Since Phase 7
(post-v0.8.46) it defaults to bridge mode: amc spawns
gdb --interpreter=mi3 itself and translates DAP↔gdb-MI
in-process so AmalgameList* / AmalgameMap* / AmalgameSet*
/ AmalgameClosure* variables pretty-print with summaries and
expandable children, and runtime helper frames (Amalgame_*,
_runtime_*, GC_*) are hidden from the call stack. On hosts
without gdb (stock macOS, etc.), amc dap falls back
transparently to the legacy proxy. amc dap --raw is the
explicit opt-out: it execvp()s into the host's DAP-native
backend (lldb-dap on macOS, gdb --dap on Linux/MSYS2 with
gdb ≥ 14), exactly like v0.8.0 used to. No source map files
either way: cgen emits #line N "foo.am" directives so DWARF
carries the .am filenames and line numbers natively.
Prerequisites
| OS | Install one of |
|---|---|
| Linux | lldb-dap from LLVM 18+ (wget https://apt.llvm.org/llvm.sh && sudo ./llvm.sh 18 && sudo apt install -y lldb-18) |
| macOS | Xcode Command Line Tools 14+ (xcode-select --install) |
| Windows | gdb 14+ via MSYS2 (pacman -S mingw-w64-x86_64-gdb) — pending v0.8.1 |
Workflow — VS Code (recommended)
- Scaffold a project with the wiring already in place:
amc new myapp --vscode # writes .vscode/launch.json + settings.json - Build with DWARF:
cd myapp && ./build.sh -g # forwards -g to `amc build` - In VS Code: open
src/main.am, click in the gutter to set a breakpoint, press F5. The F5 dropdown surfaces two pre-baked configurations — pick "Debug myapp (Linux/macOS)" or "(Windows)" depending on your platform.
The Amalgame VS Code extension (v0.3.0+) registers the amc
debug type and exposes a DebugAdapterDescriptorFactory that
spawns amc dap with the path from amalgame.dapServerPath
(or amalgame.serverPath as fallback). Logs land in the
"Amalgame DAP" output channel.
Workflow — lldb CLI (no VS Code needed)
amc build -g hello.am
lldb-18 ./hello
(lldb) breakpoint set --file hello.am --line 5
(lldb) run
(lldb) frame variable # locals visible by name + Amalgame type
(lldb) next # step over
(lldb) continue
The DWARF DW_AT_decl_file / DW_AT_decl_line fields point at
hello.am, so lldb resolves the .am source line to a real
instruction address with no extra setup.
Strategy & limits
The Phase 7 flip (post-v0.8.46) made the in-process bridge
("Approche A") the default. amc spawns
gdb --interpreter=mi3 --nx --quiet via fork + pipe + poll(),
parses MI3 records, and rewrites traffic in both directions:
AmalgameList*displays asList[N]with children[0]…[N-1];AmalgameMap*/AmalgameSet*mirror with their sizes;AmalgameClosure*displays asλ env=0x….- Stack frames matching
^Amalgame_,^_runtime_,^GC_are filtered out;stepInauto-steps until it lands on a visible frame (8-hop budget) so users never get stranded mid-runtime. amc dap --show-runtimekeeps every frame visible.
The legacy v0.8.0 proxy (Approche C) is still available as
amc dap --raw: a transparent execvp to lldb-dap on macOS
or gdb --dap on Linux/MSYS2. Useful for users hitting a
bridge regression, or for hosts where gdb isn't installed
(amc auto-falls-back in that case — no flag needed).