Kindler
Kindler Build System Specification
Version: 0.1.0 (Draft)
Last Updated: January 2026
Status: Design Phase
AI Disclosure: Claude and Grok provided assistance
1. Overview
Kindler is a declarative, cached, bootstrapped build system for UNIX-like platforms. It does not build software directly—instead, it generates build files (Makefiles, Ninja files) that native build tools execute.
Core Philosophy
- Portable: Runs on any POSIX-compliant system with Lua 5.1+
- Declarative: Project files describe what to build, not how
- Non-blocking: Warns about problems but never prevents builds
- Generator-based: Outputs to Make/Ninja, doesn't reinvent the wheel
- Cached: Bootstraps once per system, reuses platform knowledge
- Interoperable: Plays nice with other build systems
What Kindler IS
- A project description parser (UCL format)
- A platform/compiler database (hint files + cache)
- A discovery tool (bootstrap scanner)
- A build file generator (POSIX Make, GNU Make, Ninja)
- A module loader (Lua hooks for complex tasks)
What Kindler IS NOT
- A compiler (it doesn't compile anything)
- A package manager (it doesn't fetch dependencies)
- A scripting language (no Turing-complete logic in project files)
- A build executor (Make/Ninja do the actual building)
- A replacement for CMake/Meson for all use cases
2. Architecture
2.1 Bootstrap Phase
Purpose: One-time system profiling to build a knowledge cache.
Steps:
- Detect OS via
unameand match against OS hints - Load OS-specific hints file (
hints/os/<osname>.lua) - Scan for compilers listed in OS hints
- Detect available libraries (via pkg-config/pkgconf)
- Optionally scan system headers
- Serialize all findings to cache (
~/.config/kindler/cache/<hostname>.lua)
Cache Format: Lua source files (portable, debuggable)
Cache Invalidation: 30-day TTL or manual re-bootstrap
2.2 Project Files (UCL Format)
Projects are described in UCL (Universal Configuration Language), a human-readable, non-Turing-complete format.
Example:
ucl
project {
name = "myapp";
lang = "c99";
version = "1.0.0";
}
dependencies {
requires = ["mbedtls >= 2.16", "pthread"];
prefer = "static"; # or "shared", "any"
}
build {
sources = ["main.c", "net.c", "util.c"];
output = "myapp";
type = "executable"; # or "static", "shared"
}
config {
debug {
cflags = ["-g", "-O0"];
defines = ["DEBUG=1"];
};
release {
cflags = ["-O2"];
defines = ["NDEBUG"];
};
}
Key Sections:
project— Name, language, metadatadependencies— External libraries (resolved via pkg-config + OS hints)build— Source files, output, artifact typeconfig— Build configurations (debug, release, custom)modules— Optional Lua modules for complex tasks
2.3 Hints System
Hints are Lua tables that describe platform/compiler capabilities.
OS Hints (hints/os/<osname>.lua)
lua
return {
irix65 = {
posix = "yes",
architectures = {"sgi-mips", "sgi-mips64"},
compilers = {"mipspro", "gcc"}, -- native first
endian = "big",
abi_list = {"n32", "o32", "n64"}, -- default first
include_path = "/usr/include",
library_search_paths = {"/usr/lib", "/usr/lib32", "/usr/lib64"},
standard_libs = {"c", "m", "pthread"},
headers = {"stdio.h", "stdlib.h", "unistd.h", ...},
}
}
Compiler Hints (hints/compiler/<compiler>.lua)
lua
return {
mipspro = {
architecture = {"sgi-mips", "sgi-mips64"},
["compiler.drivers"] = {"/usr/bin/cc", "/usr/bin/c99", "/usr/bin/CC"},
languages = {"ansi-c", "c99", "c++98", "c++03"},
["ansi-c.driver"] = "cc", -- Use `cc` for ANSI C
["c99.driver"] = "c99", -- Use `c99` for C99 (NOT cc -std=c99!)
["c++98.driver"] = "CC",
version_flag = "-version",
warning_flags = "-fullwarn",
pic_flag = "-KPIC",
shared_link_flags = "-shared",
rpath_flag = "-rpath",
uses_ranlib = "no",
atomic_builtins = "no",
thread_local = "no",
}
}
```
**Key Principle:** Hints describe *capabilities*, not build commands. The generator converts hints → build rules.
---
### 2.4 Dependency Resolution
**Strategy:**
1. Parse `dependencies.requires` from project file
2. Check OS hints for `library_mappings` (platform-specific knowledge)
3. Query pkg-config/pkgconf for library flags
4. Fall back to generic search in `library_search_paths`
5. Respect `dependencies.prefer` (static/shared/any)
6. Warn if library not found, but don't block
**Example Flow:**
```
User requests: "mbedtls >= 2.16"
↓
Check OS hints: library_mappings.mbedtls → {pkg_config = "mbedtls"}
↓
Run: pkg-config --modversion mbedtls → "2.28.0" ✓
↓
Run: pkg-config --libs --static mbedtls → "-lmbedtls -lmbedx509 -lmbedcrypto"
↓
Cache result: mbedtls.libs = {...}
2.5 Build File Generation
Supported Targets:
- POSIX Make (maximum portability)
- GNU Make (extensions like pattern rules)
- Ninja (fast incremental builds)
Generator Responsibilities:
- Read project file + cache
- Resolve dependencies
- Select compiler based on language + OS hints
- Construct CFLAGS/LDFLAGS from hints + config
- Output build rules to target format
Example Output (POSIX Make):
make
# Generated by Kindler v0.1.0
# Config: debug
CC = /usr/bin/c99
CFLAGS = -g -O0 -DDEBUG=1 -fullwarn
LDFLAGS =
LIBS = -lmbedtls -lmbedx509 -lmbedcrypto -lpthread
OBJS = main.o net.o util.o
all: myapp
myapp: $(OBJS)
$(CC) $(OBJS) $(LIBS) -o myapp $(LDFLAGS)
.c.o:
$(CC) $(CFLAGS) -c $
clean:
rm -f $(OBJS) myapp
2.6 Module System
For complex tasks that exceed UCL's capabilities, users write Lua modules that hook into build phases.
Module Example:
lua
-- modules/version_gen.lua
return {
name = "version_gen",
hooks = {"pre-build"},
["pre-build"] = function(context)
-- Generate version.h from git tags
local handle = io.popen("git describe --tags")
local version = handle:read("*a"):gsub("\n", "")
handle:close()
local f = io.open("version.h", "w")
f:write('#define VERSION "' .. version .. '"\n')
f:close()
print("Generated version.h: " .. version)
end
}
Hook Phases:
pre-parse— Before reading project filepost-parse— After parsing, before generationpre-build— After Makefile generation
Loading Modules:
ucl
modules {
load = ["version_gen", "custom_checks"];
}
3. Supported Platforms (v0.1.0)
Target Systems:
- IRIX 6.5.x (MIPS, MIPS64)
- GNU/Linux (glibc 2.17+)
- NetBSD 9.0+
- FreeBSD 13.0+
- Solaris 11+ / OpenIndiana (if testable)
Required Tools:
- Lua 5.1, 5.2, 5.3, or 5.4
- POSIX shell (
/bin/sh) uname,test,mkdir- pkg-config or pkgconf (recommended)
- Make or Ninja
4. Design Decisions
4.1 Why UCL?
- Human-readable (better than JSON, simpler than YAML)
- Not Turing-complete (forces separation of config vs. logic)
- Lua-parseable (no external dependencies)
4.2 Why Generate, Not Build?
- Leverage decades of Make/Ninja optimization
- Interoperate with existing toolchains
- Users get native incremental builds "for free"
4.3 Why Lua Source Caches?
- Portable across architectures (unlike bytecode)
- Fast to parse (Lua's parser is optimized)
- Debuggable (users can inspect/edit caches)
4.4 Why Non-Blocking Warnings?
- Developers know their toolchains better than Kindler does
- Experimental builds should be possible
- Kindler provides info, humans make decisions
4.5 Why Per-Driver Language Selection?
- Compilers like MIPSpro use different binaries for C89 vs C99
- GCC-style
-std=flags are not universal - Hints encode the "right way" per compiler
5. Limitations & Non-Goals
Limitations:
- No dependency fetching (use system packages or vendor code)
- No cross-compilation auto-detection (user must bootstrap target system)
- No built-in testing framework (integrate with existing tools)
- No IDE integration (yet)
Non-Goals:
- Replace CMake/Meson for GUI apps or complex projects
- Compete with Bazel/Buck for monorepos
- Support Windows natively (WSL/Cygwin maybe)
- Implement a package manager
6. Workflow Example
bash
# One-time bootstrap (per machine)
$ kindler bootstrap
Detecting OS... IRIX 6.5.30
Found compilers: mipspro (/usr/bin/cc), gcc (/usr/local/bin/gcc)
Scanning headers... 342 found
Cache written: ~/.config/kindler/cache/sgi-onyx.lua
# Generate build files
$ cd myproject/
$ kindler generate --config=debug --target=make
Reading: project.ucl
Resolving dependencies... mbedtls [OK], pthread [OK]
Generating: Makefile (POSIX)
# Build (using native tools)
$ make
cc -g -O0 -DDEBUG=1 -c main.c
cc -g -O0 -DDEBUG=1 -c net.c
cc main.o net.o -lmbedtls -lpthread -o myapp
# Switch configs
$ kindler generate --config=release --target=ninja
Generating: build.ninja (Ninja)
$ ninja
[2/2] Linking myapp
7. Future Considerations (Post-v1.0)
- IDE plugins (VSCode, Vim)
- CI/CD templates
- Cross-compilation wizard
- Hint database versioning
- Community hint repository