Back to Kindler Main Page

Kindler Migration Guide

Kindler Logo

This guide helps you migrate existing projects from CMake or Meson to Kindler.


Table of Contents


Philosophy: What's Different

CMake and Meson: Imperative + Declarative

Both CMake and Meson allow scripting logic in build files:

# CMake
if(UNIX)
  set(SOURCES ${SOURCES} unix.c)
endif()

foreach(file ${SOURCES})
  message("Source: ${file}")
endforeach()
# Meson
if host_machine.system() == 'linux'
  sources += ['linux.c']
endif

Kindler: Purely Declarative

Kindler has no conditionals, loops, or functions in project files:

# Kindler
build {
    sources = ["main.c", "unix.c", "linux.c"];
}

If you need conditional logic, use:

Key Differences

Feature CMake/Meson Kindler
Scripting in build files Yes No (use modules)
Builds directly Yes No (generates Makefiles)
Dependency detection Built-in (find_package, dependency()) pkgconf + OS hints
Cross-compilation Complex toolchain files Bootstrap target, copy cache
IDE integration Extensive None (generates standard Makefiles)
Learning curve Moderate to steep Shallow

Migrating from CMake

Basic Executable

CMake (CMakeLists.txt):

cmake_minimum_required(VERSION 3.10)
project(myapp C)

add_executable(myapp main.c util.c)

Kindler (myapp.kindler):

project {
    name = "myapp";
    lang = "c99";
}

build {
    sources = ["main.c", "util.c"];
}

With Dependencies

CMake:

find_package(ZLIB REQUIRED)
find_package(Threads REQUIRED)

add_executable(myapp main.c)
target_link_libraries(myapp ZLIB::ZLIB Threads::Threads)

Kindler:

project {
    name = "myapp";
    lang = "c99";
}

dependencies {
    requires = ["zlib", "pthread"];
}

build {
    sources = ["main.c"];
}

Compiler Flags and Defines

CMake:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
add_definitions(-DDEBUG -DVERSION="1.0")
target_include_directories(myapp PRIVATE include/)

Kindler:

build {
    sources = ["main.c"];
    includes = ["include/"];
    defines = ["DEBUG", "VERSION=\"1.0\""];
}

config {
    release {
        cflags = ["-O2", "-Wall", "-Wextra"];
    };
}

Debug vs Release Builds

CMake:

set(CMAKE_C_FLAGS_DEBUG "-g -O0")
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")

# Build with:
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake -DCMAKE_BUILD_TYPE=Release ..

Kindler:

config {
    debug {
        cflags = ["-g", "-O0"];
    };
    release {
        cflags = ["-O2"];
        defines = ["NDEBUG"];
    };
}

# Generate with:
kindler.lua generate --config=debug
kindler.lua generate --config=release

Static Library

CMake:

add_library(mylib STATIC lib.c util.c)
target_include_directories(mylib PUBLIC include/)

install(TARGETS mylib DESTINATION lib)
install(DIRECTORY include/ DESTINATION include)

Kindler:

project {
    name = "mylib";
    lang = "c99";
}

build {
    sources = ["lib.c", "util.c"];
    output = "libmylib.a";
    type = "static";
    includes = ["include/"];
}

install {
    prefix = "/usr/local";
    includedir = "include";
}

Shared Library

CMake:

add_library(mylib SHARED lib.c)
set_target_properties(mylib PROPERTIES VERSION 1.0.0)

Kindler:

project {
    name = "mylib";
    lang = "c99";
    version = "1.0.0";
}

build {
    sources = ["lib.c"];
    output = "libmylib.so";
    type = "shared";
}

config {
    release {
        cflags = ["-O2", "-fPIC"];
    };
}

Platform Detection and config.h

CMake:

include(CheckIncludeFile)
include(CheckFunctionExists)

check_include_file(unistd.h HAVE_UNISTD_H)
check_function_exists(strlcpy HAVE_STRLCPY)

configure_file(config.h.in config.h)

config.h.in:

#cmakedefine HAVE_UNISTD_H
#cmakedefine HAVE_STRLCPY

Kindler:

config-header {
    output = "config.h";
    platform = "auto";
    
    check-headers = ["unistd.h"];
    check-functions = ["strlcpy"];
}

No template file needed; Kindler generates config.h directly.

Installation

CMake:

install(TARGETS myapp DESTINATION bin)
install(FILES myapp.1 DESTINATION share/man/man1)

# Install with:
cmake --install . --prefix /usr/local

Kindler:

install {
    prefix = "/usr/local";
    mandir = "share/man/man1";
}

# Install with:
make install
# or
make install PREFIX=/opt/myapp

Subdirectories

CMake:

# Top-level CMakeLists.txt
add_subdirectory(src)
add_subdirectory(lib)

# src/CMakeLists.txt
add_executable(myapp main.c)
target_link_libraries(myapp mylib)

Kindler:

# Single project file lists all sources
build {
    sources = [
        "src/main.c",
        "lib/mylib.c"
    ];
}

Or create separate project files and build independently.

What CMake Features Don't Translate

CMake Feature Kindler Equivalent
if/else conditionals Use config.h or modules
foreach loops List sources explicitly
file(GLOB ...) Not supported (by design)
Custom commands Use modules with hooks
FetchContent Not supported (vendor dependencies)
find_package() pkgconf or OS hints
Target properties Direct flags in config sections

Migrating from Meson

Basic Executable

Meson (meson.build):

project('myapp', 'c')

executable('myapp', 'main.c', 'util.c')

Kindler:

project {
    name = "myapp";
    lang = "c99";
}

build {
    sources = ["main.c", "util.c"];
}

With Dependencies

Meson:

zlib_dep = dependency('zlib')
thread_dep = dependency('threads')

executable('myapp', 'main.c',
  dependencies: [zlib_dep, thread_dep])

Kindler:

project {
    name = "myapp";
    lang = "c99";
}

dependencies {
    requires = ["zlib", "pthread"];
}

build {
    sources = ["main.c"];
}

Compiler Arguments

Meson:

executable('myapp', 'main.c',
  c_args: ['-Wall', '-DDEBUG'],
  include_directories: include_directories('include'))

Kindler:

build {
    sources = ["main.c"];
    includes = ["include/"];
    defines = ["DEBUG"];
}

config {
    release {
        cflags = ["-Wall"];
    };
}

Build Types

Meson:

# Build with:
meson setup builddir --buildtype=debug
meson setup builddir --buildtype=release

Kindler:

config {
    debug {
        cflags = ["-g", "-O0"];
    };
    release {
        cflags = ["-O2"];
        defines = ["NDEBUG"];
    };
}

# Generate with:
kindler.lua generate --config=debug
kindler.lua generate --config=release

Static Library

Meson:

mylib = static_library('mylib', 'lib.c', 'util.c',
  install: true)

Kindler:

project {
    name = "mylib";
    lang = "c99";
}

build {
    sources = ["lib.c", "util.c"];
    output = "libmylib.a";
    type = "static";
}

install {
    prefix = "/usr/local";
}

Shared Library

Meson:

shared_library('mylib', 'lib.c',
  version: '1.0.0',
  install: true)

Kindler:

project {
    name = "mylib";
    lang = "c99";
    version = "1.0.0";
}

build {
    sources = ["lib.c"];
    output = "libmylib.so";
    type = "shared";
}

config {
    release {
        cflags = ["-O2", "-fPIC"];
    };
}

install {
    prefix = "/usr/local";
}

Feature Detection

Meson:

cc = meson.get_compiler('c')

conf_data = configuration_data()
conf_data.set('HAVE_UNISTD_H', cc.has_header('unistd.h'))
conf_data.set('HAVE_STRLCPY', cc.has_function('strlcpy'))

configure_file(input: 'config.h.in',
               output: 'config.h',
               configuration: conf_data)

Kindler:

config-header {
    output = "config.h";
    platform = "auto";
    
    check-headers = ["unistd.h"];
    check-functions = ["strlcpy"];
}

Installation

Meson:

install_man('myapp.1')
install_data('config.conf', install_dir: get_option('sysconfdir'))

# Install with:
meson install -C builddir

Kindler:

install {
    prefix = "/usr/local";
    mandir = "share/man/man1";
    datadir = "share/myapp";
}

# Install with:
make install

Note: Kindler does not currently auto-install man pages or data files. These must be handled separately or via a module.

What Meson Features Don't Translate

Meson Feature Kindler Equivalent
if/elif/else conditionals Use config.h or modules
foreach loops List items explicitly
Subprojects Vendor code or use system libraries
Custom targets Use modules with hooks
run_command() Use modules
get_option() Use environment variables or multiple configs
Machine introspection Bootstrap cache

Common Migration Patterns

Generated Source Files

Problem: You have code generation (e.g., version.h from git).

CMake/Meson: Use add_custom_command() or custom_target().

Kindler: Write a module:

-- modules/version_gen.lua
return {
    name = "version_gen",
    hooks = {"pre-build"},
    
    ["pre-build"] = function(context)
        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()
    end
}

Load in project file:

modules {
    load = ["version_gen"];
}

Platform-Specific Source Files

Problem: Different sources for Linux vs BSD vs Windows.

CMake/Meson: Use conditionals:

if(UNIX)
  list(APPEND SOURCES unix.c)
endif()

Kindler Option 1: Use #ifdef in source:

// platform.c
#include "config.h"

#ifdef OS_LINUX
void platform_init() { /* Linux code */ }
#elif defined(OS_FREEBSD)
void platform_init() { /* BSD code */ }
#endif

Kindler Option 2: Separate project files:

# myapp-linux.kindler
build {
    sources = ["main.c", "linux.c"];
}

# myapp-bsd.kindler
build {
    sources = ["main.c", "bsd.c"];
}

Kindler Option 3: Include all, use weak symbols or compile-time checks:

build {
    sources = ["main.c", "linux.c", "bsd.c"];
}

Each platform file uses #ifdef guards internally.

Optional Dependencies

Problem: Build with feature if library is available.

CMake:

find_package(OpenSSL)
if(OPENSSL_FOUND)
  target_link_libraries(myapp OpenSSL::SSL)
  target_compile_definitions(myapp PRIVATE HAVE_OPENSSL)
endif()

Kindler:

Option 1: Always require it:

dependencies {
    requires = ["openssl"];
}

Option 2: Use config.h to detect (not check for library, but check if headers exist):

config-header {
    check-headers = ["openssl/ssl.h"];
}

Then in code:

#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
/* Use OpenSSL */
#endif

Note: Kindler currently does not support truly optional dependencies at the build file level. Either require it or use conditional compilation.

Compiler Detection

Problem: Different flags for different compilers.

CMake:

if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Weverything")
endif()

Kindler: Compiler hints handle this automatically. Warning flags are set per-compiler in hints:

# GCC hints: warning_flags = "-Wall -Wextra"
# Clang hints: warning_flags = "-Wall -Wextra"
# MIPSpro hints: warning_flags = "-fullwarn"

You can override in config if needed:

config {
    release {
        cflags = ["-O2", "-Wall", "-Wextra", "-Wpedantic"];
    };
}

But be aware this may not work on all compilers (e.g., MIPSpro doesn't have -Wall).

Testing

Problem: You have a test suite.

CMake:

enable_testing()
add_test(NAME test1 COMMAND test1)

Meson:

test('test1', executable('test1', 'test1.c'))

Kindler: No built-in test support. Options:

  1. Separate test project file (test.kindler) that builds tests
  2. Use a module to discover and run tests
  3. Use external test harness (prove, ctest standalone, etc.)

Example with separate project:

# test.kindler
project {
    name = "test_suite";
    lang = "c99";
}

build {
    sources = ["test1.c", "test2.c"];
    output = "test_suite";
}

Build and run:

kindler.lua generate --file=test.kindler
make
./test_suite

Limitations and Workarounds

No File Globbing

CMake/Meson:

file(GLOB SOURCES "src/*.c")  # CMake
sources = run_command('ls', 'src/*.c')  # Meson

Kindler: Not supported. List files explicitly:

build {
    sources = ["src/file1.c", "src/file2.c", "src/file3.c"];
}

Workaround: Generate the list externally:

# Shell script
ls src/*.c | sed 's/^/        "/; s/$/",/' > sources.txt

Then copy into .kindler file. Or write a module to generate the project file.

No Subprojects

CMake: add_subdirectory(), ExternalProject

Meson: subproject()

Kindler: Not supported.

Workarounds:

  1. Vendor the code (copy into your project)
  2. Build subproject separately, link to installed library
  3. Use system-installed version of library

No Cross-Compilation Toolchains

CMake: Toolchain files with CMAKE_TOOLCHAIN_FILE

Meson: Cross-compilation files

Kindler: Bootstrap on target system, copy cache back to host.

Example:

# On IRIX target
kindler.lua bootstrap
# Cache written to ~/.config/kindler/cache/irix-host.lua

# Copy to Linux host
scp irix:~/.config/kindler/cache/irix-host.lua ~/.config/kindler/cache/

# On Linux, specify target cache (not yet implemented in v0.1)
kindler.lua generate --target-cache=irix-host

No Built-in Packaging

CMake: CPack

Meson: Built-in dist support

Kindler: None. Use external tools:


Running Multiple Build Systems

You don't have to choose. Many projects support both autotools and CMake, or CMake and Meson. You can do the same with Kindler.

Kindler + CMake

myproject/
  |-- CMakeLists.txt        # For CMake users
  |-- myproject.kindler     # For Kindler users
  |-- src/
      |-- main.c

Benefits:

Maintenance burden:

Kindler as Fallback

Common pattern:

myproject/
  |-- CMakeLists.txt        # Primary build system
  |-- myproject.kindler     # Fallback for exotic platforms
  |-- README.md             # Documents both

In README:

Building:

Most users should use CMake:
  cmake -B build && cmake --build build

For IRIX, Solaris, or other exotic platforms, use Kindler:
  kindler.lua bootstrap
  kindler.lua generate
  make

Migration Strategy

Recommended approach for large projects:

  1. Keep existing build system (CMake/Meson)
  2. Add .kindler file alongside
  3. Test Kindler build on target platforms
  4. Document both in README
  5. Over time, potentially phase out old system if Kindler meets all needs

Migration Checklist

Before Starting

During Migration

After Migration


Getting Help

If you run into issues during migration:


Copyright 2026 Setsuna Software L.C. and Kazuo Kuroi

Back to Kindler Main Page | Back to Setsuna Software