Sessions 83–87: grow Array/Map/String/Test ergonomics; delimiter-balanced and juxta :tt macros plus $t:type fragments; expression-level $(…),* in templates; selfhost slice lits; riscv64/aarch64 cross smoke helper and freestanding docs. Null-safe CBE type names and String_StartsWith.
14 KiB
Build and Test Guide
This guide covers building the Bux bootstrap compiler, creating projects, and running tests.
Prerequisites
- Nim (1.6+) — for building the bootstrap compiler
- C compiler (
gcc,clang, orcc) — for the C backend - Make
On Debian/Ubuntu:
sudo apt-get install nim gcc make libssl-dev
On macOS:
brew install nim gcc make openssl
Note: Crypto + TLS require OpenSSL (
-lssl -lcrypto). The build system links both automatically on the full (POSIX) runtime.
Building the Compiler
# Release build
make build
# Development build (no optimizations, faster compile)
make dev
The output is a single binary: buxc (bootstrap compiler in Nim).
The self-hosted compiler buxc2 is built from src/*.bux sources via:
make selfhost
This compiles buxc2 using the bootstrap compiler. The self-hosted compiler generates C code and invokes cc to produce native binaries.
Creating a Project
# Create a new package in a new directory
./buxc new myproject
cd myproject
# Or initialize in the current directory
mkdir myproject && cd myproject
./buxc init
This generates:
myproject/
├── bux.toml
└── src/
└── Main.bux
bux.toml
[Package]
Name = "myproject"
Version = "0.1.0"
Type = "bin"
[Build]
Output = "Bin"
Building and Running
# Type-check without building
./buxc check
./buxc check ./myproject
# Build
./buxc build
./buxc build ./myproject
# Build and run
./buxc run
./buxc run ./myproject
# Run tests (builds and runs the binary, reports pass/fail)
./buxc test
./buxc test ./myproject
# Format code
./buxc fmt src/Main.bux # single file
./buxc fmt src/ # all .bux files in directory
# Clean build artifacts
./buxc clean
Build output goes to build/ by default.
Cross-Compilation, static, and thin runtime (Linux-first)
Bux targets Linux (primary), cloud/containers, and embedded/cross. Windows is not a product focus.
# Thin runtime (no pthread / OpenSSL / sockets) — good for CLI & embed
BUX_RUNTIME=minimal ./buxc build
# Fully-static binary (implies minimal runtime; container / distroless friendly)
./buxc --static --release build
# same: BUX_STATIC=1 ./buxc --release build
# Cross-compile for ARM64 Linux (prefers aarch64-linux-gnu-gcc, else clang -target)
./buxc --static --release --target aarch64-linux-gnu build
# Cross-compile for RISC-V 64 (session 85; needs riscv64-linux-gnu-gcc)
./buxc --static --release --target riscv64-linux-gnu build
# Override C compiler
BUX_CC=aarch64-linux-gnu-gcc ./buxc --static --target aarch64-linux-gnu build
# musl fully-static (Alpine-friendly; needs musl-tools or zig)
BUX_CC=musl-gcc BUX_RUNTIME=minimal ./buxc --static --release build
# or: BUX_CC='zig cc -target x86_64-linux-musl' … (use a wrapper script)
make test-musl-static # SKIP if no musl-gcc/zig
| Switch / env | Effect |
|---|---|
BUX_RUNTIME=full |
rt/runtime.c — POSIX + OpenSSL (default on Unix) |
BUX_RUNTIME=minimal / thin / embed |
rt/runtime_minimal.c — thin single-threaded |
BUX_RUNTIME=win |
rt/runtime_win.c — historical MinGW smoke only |
--static / BUX_STATIC=1 |
-static link; defaults to minimal runtime |
--target <triple> |
Cross compile; defaults to minimal runtime |
BUX_CC |
Force C compiler binary |
BUX_CFLAGS |
Extra flags appended to the C line |
# Smoke all of the above (+ CTFE CRC + optional aarch64/riscv64 cross)
make test-linux-targets
# Build static hello for Docker scratch/distroless
./tools/build_static_hello.sh
docker build -f examples/docker/Dockerfile.static \
--build-arg BIN=build/hello_static -t bux-hello-static .
Note: Full runtime + fully-static OpenSSL is intentionally not the default (painful). Use minimal for static containers; keep full runtime for servers that need net/crypto (
nexus).
Cross toolchains
| Triple | Typical package | Smoke |
|---|---|---|
aarch64-linux-gnu |
gcc-aarch64-linux-gnu |
make test-linux-targets (SKIP if missing) |
riscv64-linux-gnu |
gcc-riscv64-linux-gnu |
same (session 85) |
clang -target <triple> alone is not enough: you still need target headers and libc
(sysroot). Prefer *-gcc from a cross package, or set BUX_CC to a wrapper that
already knows the sysroot (e.g. Zig zig cc -target …).
Freestanding / bare-metal (research spike, not v1.0)
BUX_RUNTIME=minimal / --static is the Linux userspace / container / CTFE path:
it still links against a libc (malloc, printf, strlen, …). It is not true
no-libc freestanding firmware.
| Layer | Status | Notes |
|---|---|---|
| Thin runtime (no pthread/OpenSSL) | ✅ | rt/runtime_minimal.c |
| Static musl / distroless | ✅ | make test-musl-static, Dockerfiles |
| Linux multi-arch cross | ✅ | aarch64 + riscv64 smokes (SKIP without gcc) |
True freestanding (-ffreestanding, no libc) |
🔬 spike | Needs custom alloc, panic, and I/O stubs |
| Cortex-M / qemu-system | 🔬 spike | Same; plus linker scripts and startup |
Practical path today: build with BUX_RUNTIME=minimal --static --target … for
Linux userspace on foreign ISAs; treat bare-metal as a research project that
starts from a custom runtime_freestanding.c (not shipped) and does not
import Std::Net / Std::Task / OpenSSL.
Running Tests
Example suite
make test-examples # all examples/ programs (40+)
make test-errors # golden Rust-style diagnostic output
make test-stdlib # stdlib golden packages
make test-registry # package registry (local + HTTP index)
make test-apps # showcase apps build + simpledb/jwt CLI smoke (in `make test`)
make test-dwarf # #line maps + .debug_info + --release (in `make test`)
make test-linux-targets # minimal + static + aarch64/riscv64 cross (SKIP) + CTFE CRC
make test-registry # package registry local + HTTP (in `make test`)
make test-selfhost-smoke # buxc2: move_field + multi-file #line (in `make test`)
make test-lsp # hover + references/rename + call hierarchy
make bench # micro-benchmarks (Bux + C/Nim/Zig twins)
make bench-nexus # wrk throughput vs apps/nexus /api/health
Debug builds (E.4)
./buxc build # -O0 -g, #line → .bux (gdb-friendly; bootstrap)
./buxc build --release # -O2 -DNDEBUG, no #line / -g
# Selfhost (buxc2): default -O0 -g; multi-file #line from Decl.sourceFile
# (stdlib + each src/*.bux get their own path — no env needed)
export BUX_NO_LINE=1 # disable selfhost #line maps
export BUX_DEBUG_FILE=/abs/path.bux # optional: force all #line to one path
export BUX_CFLAGS="-fno-omit-frame-pointer"
gdb --args ./build/myapp
# (gdb) break Main
# (gdb) list # Bux source via #line (correct file per function)
Compiler Tests
make test
This runs:
- Example suite (
test-examples) - Error diagnostic goldens (
test-errors) - Lexer unit tests
- Parser unit tests
- Semantic analysis unit tests
- HIR lowering unit tests
- Integration tests (
buxc new,buxc --version) - Golden C-codegen tests (8 examples)
Project Tests (bux test)
./buxc test # run all tests/*.bux in the current package
./buxc test --filter first # only tests whose name contains "first"
./buxc test --filter=first _test_runner
Discovers tests/*.bux, builds each as a temp package, and runs it. Prints a
summary table and exits:
0— all selected tests passed1— at least one failure, or no tests matched the filter
Use Std::Test module for assertions inside test code.
Continuous integration
make test # full sequential suite (local)
| Workflow | When | What runs |
|---|---|---|
.github/workflows/ci.yml |
every PR + push to main |
split jobs (see below) + macOS smoke |
.github/workflows/selfhost-loop.yml |
weekly / manual / path-filtered main | make selfhost-loop |
ci.yml layout (faster PR feedback):
| Job | OS | Targets |
|---|---|---|
build |
ubuntu | make build → upload buxc artifact |
unit |
ubuntu | fmt-check + test-unit (reuse artifact) |
examples |
ubuntu | test-examples (full list) |
goldens |
ubuntu | test-errors + test-stdlib + test-registry + test-dwarf + test-drop-move + test-linux-targets |
apps |
ubuntu | test-apps |
selfhost |
ubuntu | test-selfhost-smoke |
macos |
macos-14 | rebuild + test-unit + test-examples-smoke (subset) |
windows |
windows-latest | buxc.exe + Nim unit tests + CLI + MinGW hello |
ci-gate |
ubuntu | fails if any required job failed (branch protection) |
CI speed helpers:
- Pin Nim 2.0.8; cache
.nim_runtime(big win on macOS — Nim is built from source there; Windows uses a prebuilt Nim zip) - Project-local
nimcache/viaNIMFLAGS=--nimcache:nimcache, cached per job by source hash - macOS skips full EXAMPLES (Linux already runs them) and skips
fmt-check(Linux unit job) - Windows runs
tools/smoke_windows_hello.shwith MinGW gcc +rt/runtime_win.c(no pthread/OpenSSL/ucontext). Full POSIX runtime (rt/runtime.c) remains Unix-only. Locally on Linux/macOS:BUX_RUNTIME=win ./tools/smoke_windows_hello.sh.
Parallel Linux jobs set BUX_SKIP_BUILD=1 after downloading the buxc artifact.
Locally, make test still runs the full suite sequentially and builds once.
make test-examples-smoke runs the macOS-sized subset locally.
make test includes examples, goldens, registry, apps, DWARF, unit tests, and selfhost smoke
(not the slow gen2↔gen3 fixed-point).
Selfhost loop (optional CI)
make selfhost-loop # bootstrap builds src/ twice; C+ELF match
BUX_SELFHOST_FIXED_POINT=1 make selfhost-loop # buxc2→buxc3→buxc4 fixed-point
Fixed-point compares gen2 vs gen3 (same selfhost C backend), not bootstrap
vs selfhost. Not part of default make test.
Format (bux fmt)
./buxc fmt examples/hello.bux # reformat one file
./buxc fmt lib/ # reformat a directory tree
make fmt # reformat lib/ examples/ src/ tests/ apps/
./buxc fmt --check path/ # exit 1 if any file would change
make fmt-check # CI: full-tree clean + dirty smoke
Indentation is 4 spaces by brace depth. The formatter is idempotent (safe to re-run).
make fmt-check enforces a clean tree under lib/, examples/, src/, tests/, and apps/.
Stdlib golden tests
make test-stdlib
# or: tests/stdlib_golden/run.sh ./buxc
Behavioral packages under tests/stdlib_golden/ (array, string, collections)
assert core Array/String/Map/Set/Result/Option APIs and match expected PASS lines.
API docs (bux doc)
./buxc doc lib/ # Markdown to stdout
./buxc doc --out docs/api/stdlib.md lib/
make docs # writes docs/api/stdlib.md
Scans /// line comments (and bootstrap also accepts adjacent /* */) immediately
before func / struct / enum / interface / module declarations.
Language Server (bux-lsp 0.4.0)
make lsp # → tools/bux-lsp
nim r --path:bootstrap tools/test_lsp_locals.nim
./tools/smoke_lsp_hover.sh
Features: diagnostics (buxc check), hover, go-to-def, outline, completion.
Locals are position-sensitive (nested scopes / shadowing). Inferred let types
appear on hover (let x: int · inferred).
Example Programs
make test-examples
Compiles and runs all programs in examples/.
Individual Example
mkdir -p examples_pkg/hello/src
cp examples/hello.bux examples_pkg/hello/src/Main.bux
# Create bux.toml manually or use `buxc new`
cd examples_pkg/hello && ../../buxc run
Project Layout
bux/
├── src/ # Self-hosted compiler source (Bux)
│ ├── Main.bux # Entry point
│ ├── Cli.bux # CLI commands (build, run, test, fmt, new, init)
│ ├── Lexer.bux # Tokenizer
│ ├── Parser.bux # Parser
│ ├── Ast.bux # AST definitions
│ ├── Sema.bux # Semantic analysis (borrow checker)
│ ├── Types.bux # Type system
│ ├── Scope.bux # Symbol table
│ ├── Hir.bux # High-level IR
│ ├── HirLower.bux # AST → HIR lowering
│ ├── CBackend.bux # HIR → C code generation
│ ├── Manifest.bux # bux.toml parser
│ ├── Fmt.bux # Code formatter
│ └── Token.bux # Token definitions
├── bootstrap/ # Bootstrap compiler (Nim) — compiles src/ → buxc
│ ├── main.nim
│ ├── cli.nim
│ └── ...
├── lib/ # Standard library (Bux)
│ ├── Io.bux
│ ├── Array.bux
│ ├── String.bux
│ ├── Map.bux
│ ├── Fs.bux
│ ├── Mem.bux
│ ├── Set.bux
│ ├── Path.bux
│ ├── Math.bux
│ ├── Task.bux
│ └── Channel.bux
├── rt/ # C runtime
│ ├── runtime.c # full POSIX + OpenSSL (Unix default)
│ ├── runtime_minimal.c # thin: no pthread/net/crypto (static/embed)
│ ├── runtime_win.c # MinGW historical (BUX_RUNTIME=win)
│ └── io.c
├── examples/ # Example programs
├── tests/ # Unit tests (Nim)
├── docs/ # Documentation
└── Makefile
Debugging
Verbose Output
./buxc build -v
Inspecting Generated C (bootstrap)
./buxc build
cat build/main.c
Inspecting Generated C (self-hosted)
cd src && ../buxc build
cat build/main.c
Common Errors
| Error | Cause | Fix |
|---|---|---|
stdlib directory not found |
buxc can't find lib/ |
Run from project root or set correct path |
duplicate symbol 'bux_alloc' |
Multiple stdlib modules declare same extern | Only declare in one module |
C compilation failed |
Generated C has errors | Check build/main.c for issues |
Adding a New Example
- Create
examples/myexample.bux - Add
myexampletoEXAMPLESinMakefile - Run
make test-examples
Development Workflow
# After making changes to the compiler:
make build
make test
# If tests pass, run examples:
make test-examples