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.
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.
./build.bash [app]
app defaults to lilush. Available apps: lilu, lilush, reliw, recall, zxkitty.
The built binary is placed in the repo root.
To install system-wide, do sudo mv [app] /usr/[local]/bin/
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.bash
You can also use a subdirectory name to run all tests for a module:
./run_tests.bash std
./run_tests.bash term
Or run individual test files:
./lilush tests/std/test_tbl.lua
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 Version string (substituted into {{VERSION}})
lilush.lua App config for lilush
lilu.lua App config for lilu
reliw.lua App config for reliw
recall.lua App config for recall
zxkitty.lua App config for zxkitty
entrypoints/
<app>/
*.lua Lua entrypoint code (embedded as C string constants)
main.c Custom main() (optional, per-app)
Each app config (buildgen/apps/<app>.lua) returns a table:
binary — output binary name
luamods — list of Lua module groups to include (keys into modinfo.lua)
c_libs — list of C library groups to include (keys into modinfo.lua)
start_code — table mapping C constant names to .lua file paths under
buildgen/entrypoints/. Each file is read, escaped, and emitted as a
static const char NAME[] = "..."; declaration.
custom_main (optional) — path to a .c file under buildgen/entrypoints/
containing the app's main(). If absent, buildgen/default_main.c is used.
What generate.lua does, in order:
Load config — reads the app config and modinfo.lua
Generate entrypoint constants — reads each .lua file referenced in
start_code, escapes it for C, and emits static const char declarations
Compile C modules — runs make -C src/<lib> for each C library,
strips debug symbols from the resulting .o files
Compile Lua modules — for each Lua module group, finds all .lua
files under src/<mod>/, compiles each to a bytecode header with
luajit -b, and patches the header to use the project's naming scheme
Assemble C source — fills the c_tmpl template with:
{{START_CODE}} — the entrypoint constant declarations
{{LUAMODS}} — bytecode #includes and the lua_preload[] table
{{CLIBS}} — extern declarations and the c_preload[] table
Appends the app's main() (custom or default)
Substitutes {{VERSION}} and {{APP_NAME}}
Build static library — ar rcs all .o files into liblilush.a
Link binary — clang with static linking against LuaJIT
and liblilush.a
The output is written to /build/<binary> inside the Docker container.
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.
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.