xmake — Lua-Powered Build System
xmake reference for C++ developers: xmake.lua syntax, package management via xrepo, cross-compilation, and project templates.
TL;DR
xmake is a Lua-based build system with a built-in package manager (xrepo). It requires zero external dependencies, builds fast on all three major platforms, and uses a syntax that is significantly cleaner than CMake's DSL. The trade-off is a smaller ecosystem than CMake or Meson.
- Language: Lua (you write
xmake.lua, notCMakeLists.txt) - Package manager: xrepo — pulls from its own registry, Conan, vcpkg, or system packages
- Backends: generates for Ninja, Make, VS, Xcode — or builds directly via its own runner
- No runtime deps: the xmake binary is self-contained; CI machines need no pre-installed tooling
Installation
macOS
brew install xmakeLinux (official installer)
curl -fsSL https://xmake.io/shget.text | bash
# Or with wget:
wget https://xmake.io/shget.text -O - | bashThe installer places the binary in ~/.local/bin. Add it to PATH if your shell does not pick it up automatically.
Windows
winget install xmake-io.xmakeAlternatively, download the .exe installer from xmake.io. A Scoop formula is also available:
scoop install xmakeDocker
FROM tboox/xmake:latest
WORKDIR /src
COPY . .
RUN xmake f -m release && xmakeVerify
xmake --version
# xmake v2.9.x, ...Project Structure
A minimal C++ project looks like this:
myapp/
├── xmake.lua
├── include/
│ └── myapp/
│ └── utils.h
└── src/
├── main.cpp
└── utils.cppxmake requires only xmake.lua at the root. There is no separate configure step — running xmake in the project directory both configures and builds.
xmake.lua Basics
-- xmake.lua
set_project("myapp")
set_version("1.0.0")
-- Register debug/release build modes
add_rules("mode.debug", "mode.release")
target("myapp")
set_kind("binary")
add_files("src/*.cpp")
set_languages("c++20")
add_includedirs("include")Key points:
set_projectandset_versionare optional metadata.add_rules("mode.debug", "mode.release")makes-m debugand-m releaseavailable on the command line.add_filessupports glob patterns; useremove_filesto exclude specific sources.set_languagesaccepts"c++11"through"c++23"and also C standards ("c11","c17").
Building
xmake # build default target in current mode
xmake -r # force rebuild (equivalent to clean + build)
xmake -j8 # build with 8 parallel jobs (default: auto)
xmake -v # verbose: print every compiler invocation
xmake -vD # very verbose: include diagnostic info
xmake f -m release # switch to release mode
xmake f -m debug # switch to debug mode
xmake # rebuild after mode switch
xmake run myapp # build and run the named target
xmake run myapp arg1 arg2 # pass arguments to the program
xmake clean # remove build artefacts
xmake clean -a # also remove downloaded package cachesBuild output lands in .xmake/ (config cache) and build/ (compiled artefacts) by default.
Targets: binary, static, shared
-- xmake.lua
set_project("myproject")
add_rules("mode.debug", "mode.release")
-- Static library
target("mylib")
set_kind("static") -- "shared" for a .so/.dll
add_files("src/lib/*.cpp")
add_headerfiles("include/(**.h)") -- installs headers preserving subdir structure
set_languages("c++20")
add_includedirs("include", {public = true}) -- propagate to dependants
-- Executable that links the library
target("myapp")
set_kind("binary")
add_files("src/main.cpp")
add_deps("mylib") -- automatically links and inherits public includes
set_languages("c++20")The {public = true} modifier on add_includedirs (and similarly add_defines, add_cxxflags) propagates the setting to all targets that declare add_deps on this library — the same idea as CMake's INTERFACE/PUBLIC distinction.
Package Management with xrepo
xrepo is xmake's integrated package manager. Packages are declared at the top of xmake.lua and linked with add_packages inside a target.
-- xmake.lua
add_requires("zlib", "fmt 10.2.1", "catch2")
add_requires("boost", {configs = {filesystem = true, system = true}})
target("myapp")
set_kind("binary")
add_files("src/*.cpp")
set_languages("c++20")
add_packages("zlib", "fmt", "boost")
target("tests")
set_kind("binary")
add_files("tests/*.cpp")
set_languages("c++20")
add_packages("catch2")
add_deps("myapp")
set_default(false) -- not built unless explicitly requestedxrepo CLI
xrepo install zlib # install into the global package cache
xrepo install "fmt 10.2.1" # pin a version
xrepo search boost # search the registry
xrepo info fmt # show package metadata, deps, configs
xrepo list # list installed packages
xrepo remove zlib # uninstallUsing a private or local package
-- Reference a package stored in a local directory
add_repositories("my-repo /path/to/packages")
add_requires("mypkg")Pulling packages from vcpkg or Conan
add_requires("vcpkg::sdl2")
add_requires("conan::nlohmann_json/3.11.2")Compiler Flags and Options
target("myapp")
set_kind("binary")
add_files("src/*.cpp")
set_languages("c++23")
-- Warnings
add_cxxflags("-Wall", "-Wextra", "-Wpedantic", {force = true})
-- Per-mode configuration
if is_mode("release") then
set_optimize("fastest") -- -O3
set_strip("all") -- strip debug symbols from binary
add_defines("NDEBUG")
end
if is_mode("debug") then
set_symbols("debug") -- -g
set_optimize("none") -- -O0
add_defines("DEBUG")
end
-- Platform-specific flags
if is_plat("linux") then
add_syslinks("pthread", "dl")
end
if is_plat("windows") then
add_defines("WIN32_LEAN_AND_MEAN", "NOMINMAX")
endadd_cxxflags with {force = true} passes the flags directly even if xmake would normally deduplicate or ignore them. Use add_cflags for C sources, add_ldflags for the linker, and add_arflags for the archiver.
Conditional Logic and Options
xmake.lua is real Lua, so you can use variables, loops, and conditionals freely.
-- User-configurable option (xmake f --enable_tests=y)
option("enable_tests")
set_default(false)
set_showmenu(true)
set_description("Build test suite")
option_end()
if has_config("enable_tests") then
add_requires("catch2")
includes("tests/xmake.lua") -- pull in a sub-build file
end
-- Detect a system library
if find_package("OpenSSL") then
add_defines("HAVE_OPENSSL")
add_packages("openssl")
endMulti-file Projects with includes()
For larger projects, split build definitions into subdirectory xmake.lua files and compose them with includes.
myproject/
├── xmake.lua ← root: set_project, add_requires, includes(...)
├── src/
│ └── xmake.lua ← defines target("myapp")
├── lib/
│ └── xmake.lua ← defines target("mylib")
└── tests/
└── xmake.lua ← defines target("tests")Root xmake.lua:
set_project("myproject")
set_version("2.0.0")
add_rules("mode.debug", "mode.release")
add_requires("fmt", "catch2")
includes("lib/xmake.lua")
includes("src/xmake.lua")
includes("tests/xmake.lua")Cross-Compilation
xmake has first-class cross-compilation support. Configure with xmake f before building.
Android (NDK)
xmake f -p android --ndk=/path/to/ndk -a arm64-v8a
xmakeSupported ABI values: armeabi-v7a, arm64-v8a, x86, x86_64.
iOS / tvOS
xmake f -p iphoneos -a arm64
xmake f -p iphonesimulator -a x86_64
xmakeMinGW (Windows cross-compile from Linux)
xmake f -p mingw --sdk=/path/to/mingw-w64
xmakeCustom sysroot / toolchain
-- xmake.lua
target("firmware")
set_kind("binary")
add_files("src/*.cpp")
set_toolchains("arm-linux-gnueabihf")
add_cxxflags("--sysroot=/opt/arm-sysroot")
add_ldflags("--sysroot=/opt/arm-sysroot")Or via the command line:
xmake f --toolchain=arm-linux-gnueabihf --sysroot=/opt/arm-sysrootTesting Integration
-- tests/xmake.lua
add_requires("catch2")
target("unit_tests")
set_kind("binary")
add_files("*.cpp")
add_packages("catch2")
add_deps("mylib")
set_default(false) -- excluded from a bare `xmake` run
-- Register as a test target so `xmake test` picks it up
on_run(function(target)
os.execv(target:targetfile(), {"--reporter", "compact"})
end)xmake build unit_tests # build only the test target
xmake run unit_tests # build + run
xmake test # run all registered test targets
xmake test -v # verbose output, shows individual test resultsFor GoogleTest:
add_requires("gtest")
target("gtests")
set_kind("binary")
add_files("tests/*.cpp")
add_packages("gtest")
add_deps("mylib")
set_default(false)Generating Project Files for Other Tools
xmake can emit project files so that IDEs and other build systems can consume the same xmake.lua description.
xmake project -k cmake # generate CMakeLists.txt
xmake project -k ninja # generate build.ninja
xmake project -k make # generate Makefile
xmake project -k vs2022 # generate Visual Studio 2022 solution
xmake project -k xcode # generate Xcode project
xmake project -k compile_commands # generate compile_commands.json (for clangd)The compile_commands.json target is the most useful for day-to-day development — it feeds clangd, clang-tidy, and most LSP-based editors.
xmake project -k compile_commands
ln -s build/compile_commands.json . # put it where editors expect itCustom Rules and Tasks
Rules let you teach xmake how to handle non-standard source types.
-- Compile GLSL shaders to SPIR-V during the build
rule("glsl")
set_extensions(".vert", ".frag", ".comp", ".glsl")
on_build_file(function(target, sourcefile, opt)
local outfile = sourcefile .. ".spv"
os.run("glslc %s -o %s", sourcefile, outfile)
print("compiled shader: %s -> %s", sourcefile, outfile)
end)
target("game")
set_kind("binary")
add_rules("glsl") -- attach the custom rule
add_files("shaders/*.vert")
add_files("shaders/*.frag")
add_files("src/*.cpp")
set_languages("c++20")Custom tasks (like Makefile phony targets):
task("lint")
on_run(function()
os.run("clang-tidy $(find src -name '*.cpp') -- -std=c++20")
end)
set_menu {description = "Run clang-tidy on all sources"}xmake lint # invoke the custom taskToolchain Selection
xmake auto-detects the system compiler, but you can pin a specific toolchain:
xmake f --toolchain=clang # use clang/clang++
xmake f --toolchain=gcc # use gcc/g++
xmake f --toolchain=msvc # use MSVC (Windows)
xmake f --toolchain=icc # Intel C++ CompilerInside xmake.lua:
-- Only use clang on Linux
if is_plat("linux") then
set_toolchains("clang")
endCheck what toolchains are available:
xmake show -l toolchainsxmake vs CMake vs Meson
| Feature | xmake | CMake | Meson |
|---|---|---|---|
| Config language | Lua (clean) | CMake DSL (verbose) | Python-like (clean) |
| Built-in pkg manager | Yes (xrepo) | No (needs vcpkg/Conan) | Wraps (limited) |
| Package ecosystem | Growing | Largest | Medium |
| Out-of-source builds | Optional | Optional | Mandatory |
| Windows support | Excellent | Excellent | Good |
| Cross-compilation | First-class | Via toolchain files | First-class |
| IDE integration | Good | Excellent | Good |
| Learning curve | Low | High | Low |
| CI/CD adoption | Limited | Ubiquitous | Moderate |
| Generator backends | Ninja, Make, VS, Xcode | Ninja, Make, VS, Xcode | Ninja only |
When to choose xmake:
- You want a CMake-free stack with built-in package management.
- Your team is comfortable with Lua and wants readable build files.
- You are targeting embedded or mobile platforms and want easy cross-compilation.
When to stay with CMake:
- You need to integrate with projects or libraries that already ship
CMakeLists.txt(nearly all of the C++ ecosystem). - Your CI/CD infrastructure, IDE plugins, and colleagues all assume CMake.
Common Pitfalls
add_files globs are evaluated at configure time. Adding new .cpp files to a directory does not automatically include them; you must run xmake f again (or simply xmake, which re-evaluates).
Mode state is sticky. After xmake f -m release, subsequent bare xmake invocations use release mode until you switch back. The current config is stored in .xmake/.
{public = true} is not the default. Unlike CMake's modern INTERFACE targets, xmake properties are private by default. You must explicitly opt into propagation.
Package versions are fuzzy by default. add_requires("fmt") resolves to the latest compatible version. Pin with add_requires("fmt 10.2.1") for reproducible builds.
See Also
- CMake — the dominant C++ build system; the xmake ecosystem is measured against it
- Meson — similar philosophy to xmake (clean syntax, fast), uses Python-like DSL
- Ninja — low-level build executor that xmake can target as a backend
- vcpkg — Microsoft's package manager; xrepo can delegate to it
- Conan — the other major C++ package manager; also accessible from xrepo
- Cross-Compilation — broader guide to targeting non-host platforms