Kindler

From TechPubs Wiki

Revision as of 05:45, 26 January 2026 by Raion (talk | contribs) (Created page with "= 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:''...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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

  1. Portable: Runs on any POSIX-compliant system with Lua 5.1+
  2. Declarative: Project files describe what to build, not how
  3. Non-blocking: Warns about problems but never prevents builds
  4. Generator-based: Outputs to Make/Ninja, doesn't reinvent the wheel
  5. Cached: Bootstraps once per system, reuses platform knowledge
  6. 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:

  1. Detect OS via uname and match against OS hints
  2. Load OS-specific hints file (hints/os/<osname>.lua)
  3. Scan for compilers listed in OS hints
  4. Detect available libraries (via pkg-config/pkgconf)
  5. Optionally scan system headers
  6. 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, metadata
  • dependencies — External libraries (resolved via pkg-config + OS hints)
  • build — Source files, output, artifact type
  • config — 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 file
  • post-parse — After parsing, before generation
  • pre-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