Build & Test Systems
Build system
Lilush uses a custom build generator (buildgen/generate.lua) that compiles
Lua modules into LuaJIT bytecode, links them with C libraries, and produces
a single static binary. The generator itself runs on plain LuaJIT with no
external Lua dependencies.
Docker is used purely as a build medium — it provides the Alpine toolchain and LuaJIT, then runs the generator. No runtime images are produced.
Testing system
Lilush uses a minimal custom test framework called testimony (src/testimony/) for
regression prevention and refactoring safety.
Tests live in tests/ and organized into subdirs per module.
Building
./build.bash [app]
app defaults to lilush. In-tree apps: lilu, lilush
(each has a buildgen/apps/<app>.lua config).
The built binary is placed in the repo root.
To install system-wide, do sudo mv [app] /usr/[local]/bin/
Building an external LILPACK into a custom binary
Some apps live in their own LILPACK repos rather than in-tree (e.g. RECALL), yet can still be compiled into a custom, tailored static binary instead of being installed at runtime:
./build.bash --pack <git-url> --ref <tag-or-sha> --name <binary>
The pack is cloned at --ref (use an immutable tag or commit SHA) and built
from its own build/<name>.lua descriptor. The pack's own Lua modules are
auto-scanned from its tree (names derived from file paths, exactly like the
LILPACK packer); the descriptor only lists the shared lilush module groups
(luamods/c_libs) it depends on. The same repo can also be lilpack packed
into a signed .lilpack for runtime install — one source, two outputs.
A pack binary reports its own version via -v, taken from the pack's
lilpack.json, alongside the lilush toolchain it was built against — e.g.
recall -v prints recall 0.3.1 (lilush 0.8.5). In-tree builds (lilush,
lilu) keep the plain version 0.8.5 form.
Running Tests
Testing after recent changes
After making changes, build lilush and run:
For running all tests (requires the binary to be in the repo root, which is default after build):
./run_tests.bashYou can also use a subdirectory name to run all tests for a module:
./run_tests.bash std ./run_tests.bash termOr run individual test files:
./lilush tests/std/test_tbl.lua
File layout
Dockerfile Docker build template (takes APP build arg)
build.bash Build entry point
buildgen/
generate.lua Build orchestrator (runs under luajit)
c_tmpl C source template with preload infrastructure
modinfo.lua Module registry (Lua module names -> C identifiers)
default_main.c Default main() for apps without a custom one
apps/
version Lilush toolchain version (drives {{LILUSH_VERSION}} and the {{VERSION_STRING}} shown by -v)
lilush.lua App config for lilush
lilu.lua App config for lilu
entrypoints/
<app>/
*.lua Lua entrypoint code (embedded as C string constants)
main.c Custom main() (optional, per-app)
App configs
Each app config (buildgen/apps/<app>.lua) returns a table:
binary— output binary nameluamods— list of Lua module groups to include (keys intomodinfo.lua)c_libs— list of C library groups to include (keys intomodinfo.lua)start_code— table mapping C constant names to.luafile paths underbuildgen/entrypoints/. Each file is read, escaped, and emitted as astatic const char NAME[] = "...";declaration.custom_main(optional) — path to a.cfile underbuildgen/entrypoints/containing the app'smain(). If absent,buildgen/default_main.cis used.
Build pipeline
What generate.lua does, in order:
Load config — reads the app config and
modinfo.luaGenerate entrypoint constants — reads each
.luafile referenced instart_code, escapes it for C, and emitsstatic const chardeclarationsCompile C modules — runs
make -C src/<lib>for each C library, strips debug symbols from the resulting.ofilesCompile Lua modules — for each Lua module group, finds all
.luafiles undersrc/<mod>/, compiles each to a bytecode header withluajit -b, and patches the header to use the project's naming schemeAssemble C source — fills the
c_tmpltemplate with:{{START_CODE}}— the entrypoint constant declarations{{LUAMODS}}— bytecode#includes and thelua_preload[]table{{CLIBS}}—externdeclarations and thec_preload[]tableAppends the app's
main()(custom or default)Substitutes
{{VERSION_STRING}}(the-vline),{{LILUSH_VERSION}}(exposed to Lua as theLILUSH_VERSIONglobal), and{{APP_NAME}}
Build static library —
ar rcsall.ofiles intoliblilush.aLink binary —
clangwith static linking against LuaJIT andliblilush.a
The output is written to /build/<binary> inside the Docker container.
Docker
Dockerfile accepts a single build arg APP (the app name).
It installs the Alpine build toolchain, compiles LuaJIT from
source, copies src/ and buildgen/ into the container, and runs
generate.lua apps/${APP}.lua.
build.bash builds the Docker image, copies the binary out, and cleans up
the container.
Build dependencies
Provided by the Docker image (Alpine):
clang(compiler/linker)alpine-sdk(make, ar, strip, etc.)git(to clone LuaJIT)linux-headers(kernel headers for C modules)
Built from source inside Docker:
LuaJIT (
v2.1) — built with-DLUAJIT_DISABLE_FFI -DLUAJIT_ENABLE_LUA52COMPAT
Cryptographic primitives are provided by LITLS (src/litls/), which ships
in-tree and requires no external library installation.