
This guide helps you migrate existing projects from CMake or Meson to Kindler.
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 has no conditionals, loops, or functions in project files:
# Kindler
build {
sources = ["main.c", "unix.c", "linux.c"];
}
If you need conditional logic, use:
| 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 |
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"];
}
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"];
}
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"];
};
}
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
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";
}
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"];
};
}
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.
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
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.
| 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 |
Meson (meson.build):
project('myapp', 'c')
executable('myapp', 'main.c', 'util.c')
Kindler:
project {
name = "myapp";
lang = "c99";
}
build {
sources = ["main.c", "util.c"];
}
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"];
}
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"];
};
}
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
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";
}
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";
}
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"];
}
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.
| 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 |
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"];
}
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.
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.
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).
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:
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
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.
CMake: add_subdirectory(), ExternalProject
Meson: subproject()
Kindler: Not supported.
Workarounds:
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
CMake: CPack
Meson: Built-in dist support
Kindler: None. Use external tools:
tar czf myapp-1.0.tar.gz src/dpkg-buildpackageYou don't have to choose. Many projects support both autotools and CMake, or CMake and Meson. You can do the same with Kindler.
myproject/
|-- CMakeLists.txt # For CMake users
|-- myproject.kindler # For Kindler users
|-- src/
|-- main.c
Benefits:
Maintenance burden:
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
Recommended approach for large projects:
If you run into issues during migration:
Copyright 2026 Setsuna Software L.C. and Kazuo Kuroi