| name | xmake |
| description | XMake build configuration, options, commands, and patterns for LuisaCompute. |
XMake Build System
Primary build system. Requires XMake 3.0.6+. Optional: CUDA Toolkit, Vulkan SDK, LLVM 20, Rust.
Quick Start
xmake f -m debug -c -y
xmake build
xmake project -k compile_commands --lsp=clangd .vscode
Configuration
| Platform | Command |
|---|
| Linux GCC | xmake f -p linux -a x86_64 --toolchain=gcc -m release -c |
| Linux Clang | xmake f -p linux -a x86_64 --toolchain=clang -m release -c |
| Windows MSVC | xmake f -p windows -a x64 --toolchain=msvc -m release -c |
| Windows Clang-CL | xmake f -p windows -a x64 --toolchain=clang-cl -m release -c |
| Windows LLVM | xmake f -p windows -a x64 --toolchain=llvm -m release -c |
| macOS Clang | xmake f -p macosx -a arm64 --toolchain=clang -m release -c |
Flags
-c clean cache, -m <mode> (release/debug/releasedbg/check/profile/coverage), -p <plat> (linux/windows/macosx), -a <arch> (x86_64/x64/arm64), --check check before building, -y auto-accept all prompts and skip interaction (useful in scripts/CI).
In this project debug mode automatically enables AddressSanitizer (ASan). To enable ASan for other modes, use --policies=build.sanitizer.address.
Sanitizer Modes
XMake supports sanitizer builds through sanitizer policies. Policies propagate the sanitizer configuration to dependent packages and avoid the deprecation warnings produced by the legacy mode.asan/mode.tsan/mode.lsan/mode.ubsan rules.
ASan in debug mode
Configure and build with debug mode as usual:
xmake f -m debug -c -y
xmake build
xmake run <target>
Enable via policy manually
To enable a sanitizer for a different mode, use the corresponding policy. In xmake.lua:
set_policy("build.sanitizer.address", true)
Or from the command line:
xmake f --policies=build.sanitizer.address -c -y
xmake build
xmake run <target>
Available policies:
| Policy | Sanitizer |
|---|
build.sanitizer.address | AddressSanitizer |
build.sanitizer.thread | ThreadSanitizer |
build.sanitizer.memory | MemorySanitizer |
build.sanitizer.leak | LeakSanitizer |
build.sanitizer.undefined | UndefinedBehaviorSanitizer |
Multiple sanitizers can be combined, e.g.:
xmake f --policies=build.sanitizer.address,build.sanitizer.undefined -c -y
Commands
| Command | Description |
|---|
xmake clean | Clean |
xmake -r | Rebuild |
xmake build <target> | Build target |
xmake run <target> | Run target |
xmake run <target> <args> | Run target with arguments |
xmake -l | List targets |
xmake install -o <dir> | Install binaries to <dir> |
xmake -y | Auto-accept all prompts (downloads, overwrites, etc.), skip interaction |
xmake project -k compile_commands --lsp=clangd .vscode | Generate compile_commands.json |
Common Issues
-v, -D, --diagnosis invalid; use --verbose
- Boolean options:
--lc_option=true/=false
- Use
-c to clean cache when reconfiguring with different options
- Use
-y to auto-accept all prompts and skip interaction — essential in automated scripts and CI pipelines
lc_fallback_backend requires both lc_llvm_path and lc_embree_path
lc_dx_backend is silently disabled on non-Windows platforms
lc_metal_backend is silently disabled on non-macOS platforms
lc_cuda_backend is silently disabled outside Windows/Linux
- PCH (precompiled header) error like
has been modified since the precompiled header / redefinition of ... means the target's PCH is stale — use xmake build -r <target> to force a clean rebuild of that target.
Xmake Target Writing Tutorial
Overview
This tutorial covers how to write xmake targets using the standard xmake API, with examples drawn from real projects like LuisaCompute. The recommended style uses on_load callbacks for dynamic configuration, with static declarations outside.
1. Basic Target Structure
target("<name>", {kind = "static"})
target("<name>")
set_kind("static")
set_basename("my-lib")
add_deps("dep1", "dep2")
add_rules("my-rule")
add_files("src/*.cpp")
add_headerfiles("include/**.h")
on_load(function(target)
target:add("includedirs", "include", {public = true})
target:add("defines", "MY_DEFINE", {public = true})
target:add("deps", "another-dep")
target:set("kind", "shared")
target:add("links", "pthread")
target:add("syslinks", "dl")
target:add("packages", "spdlog")
end)
after_build(function(target)
)
target_end()
Key rules:
add_rules() must be outside on_load — they are target-level; cannot be set from inside on_load.
add_deps() outside = target:add("deps", ...) inside — they are equivalent.
- Simple globs (
add_files, add_headerfiles) can go outside; conditional additions go inside on_load.
- Visibility — pass
{public = true}, {interface = true}, or {private = true} (default) to control inheritance.
2. Config Fields and Script Fields
Every xmake target has two kinds of declarations:
- Config fields — static target properties (what to build, how to build it).
- Script fields — lifecycle callbacks (when to run custom Lua code).
Config Fields
Config fields are the key/value pairs that describe a target. They are set with set_* / add_* outside on_load, or equivalently with target:set() / target:add() inside a script field.
Common config field categories:
| Category | Fields |
|---|
| Identity | kind, basename, filename, prefixname, suffixname, extension, group |
| Output | targetdir, objectdir, dependir, rundir, installdir, prefixdir |
| Sources | files, headerfiles, extrafiles, remove_files, configfiles, installfiles |
| Includes | includedirs, sysincludedirs |
| Defines | defines, undefines, configvar |
| Links | links, syslinks, linkdirs, rpathdirs, linkorders, linkgroups, frameworks, frameworkdirs |
| Compilation | languages, optimize, warnings, symbols, runtimes, exceptions, fpmodels, encodings, strip, vectorexts, forceincludes, pcheader, pcxxheader |
| Dependencies | deps, packages, options, rules |
| Misc | values.*, runenv, runargs, enabled, default, toolchains, toolset, plat, , |
Rules for config fields:
- Most config fields can be set either outside or inside
on_load using the equivalent target:set("field", value) / target:add("field", value) form.
add_rules() must be outside on_load — rules are target-level metadata and cannot be added from inside a script field.
- Static config goes outside for readability; dynamic/conditional config goes inside
on_load.
- Use
{public = true} / {interface = true} / {private = true} with target:add() to control inheritance of includedirs, defines, links, etc.
target:get("field") reads a config field inside a script field; has_config("opt") reads project-level options.
Script Fields
Script fields are the lifecycle hooks where you write imperative Lua code. They receive the target object (and sometimes other arguments) and run at specific build phases.
Common script fields:
| Script field | Runs when | Typical use |
|---|
on_load(function(target) ... end) | Target is loaded (early) | Dynamic config, conditional deps/files |
on_config(function(target) ... end) | After xmake config, before build | Validate toolchain/options |
before_build(function(target) ... end) | Before compilation starts | Pre-build checks/code generation |
on_build(function(target) ... end) | Build phase | Override entire build |
after_build(function(target) ... end) | After build finishes | Copy outputs, print reports |
before_link(function(target) ... end) | Before linking | Inject link args |
after_link(function(target) ... end) | After linking | Sign/post-process binary |
on_install(function(target) ... end) | Install phase | Custom install logic |
on_run(function(target) ... end) | xmake run | Override run behavior |
Rules for writing code in script fields:
- Always operate on the
target argument for target-local config: target:add("field", value), target:set("field", value), target:get("field").
- Project-scope helpers are still available:
is_plat(), is_arch(), is_mode(), has_config(), get_config(), os.*, io.*, path.*, etc.
- You can
import() extension modules at the top of the script field callback (or at file scope).
on_load is for configuration — it should set/add target config fields. It runs very early, so dependencies may not be fully resolved yet.
before_build / after_build are for actions — they run around compilation and are the right place to generate files, copy DLLs, run validators, or emit summaries.
- Returning
false from some hooks (e.g. on_test) signals failure; most hooks ignore return values.
Example: Writing Code Inside Script Fields
target("my-scripted-target")
set_kind("binary")
add_files("src/*.cpp")
add_includedirs("include")
set_basename("myapp")
set_warnings("all")
on_load(function(target)
target:add("defines", "VERSION=\"1.0.0\"", {public = true})
if target:is_plat("windows") then
target:add("syslinks", "Advapi32", "Ole32")
elseif target:is_plat("linux") then
target:add("syslinks", "pthread", "dl")
end
if is_mode("debug") then
target:set("symbols", "debug")
target:set("optimize", "none")
end
target:data_set("build_start", os.mclock())
end)
before_build(function(target)
local main = path.join(target:scriptdir(), "src/main.cpp")
if not os.isfile(main)
raise( .. main)
out = .join(target:autogendir(), )
.mkdir(.directory(out))
.writefile(out, .(, .()))
target:add(, .directory(out))
)
after_build(
exe = target:targetfile()
.isfile(exe)
dest = .join(, )
.mkdir(dest)
.cp(exe, dest)
(, exe)
start = target:data()
start
(, .mclock() - start, )
)
target_end()
3. API Equivalence: Inside on_load
Use target:add() and target:set() inside on_load to dynamically configure targets:
target:add() — cumulative (equivalent to add_*)
Inside on_load(target) |
|---|
target:add("deps", "foo") |
target:add("files", "*.cpp") |
target:add("headerfiles", "*.h") |
target:add("includedirs", "inc") |
target:add("sysincludedirs", "inc") |
target:add("defines", "FOO") |
target:add("undefines", "BAR") |
target:add("links", "foo") |
target:add("syslinks", "dl") |
target:add("linkorders", ...) |
target:add("linkgroups", {group = true}) |
target:add("linkdirs", "lib") |
target:add("rpathdirs", "lib") |
target:add("frameworks", "Foundation") |
target:add("frameworkdirs", "dir") |
target:add("embeddirs", "dir") |
target:add("packages", "spdlog") |
target:add("options", "myopt") |
target:add("vectorexts", "avx2") |
target:add("languages", "cxx20") |
target:add("imports", "module") |
target:add("runenvs", "PATH", "/usr/bin") |
target:add("forceincludes", "inc.h") |
target:add("configfiles", "config.h.in") |
target:add("installfiles", "data/*") |
target:add("extrafiles", "readme.md") |
target:add("filegroups", "src", files) |
target:set() — singular (equivalent to set_*)
Inside on_load(target) |
|---|
target:set("kind", "static") |
target:set("basename", "foo") |
target:set("filename", "foo.dll") |
target:set("prefixname", "lib") |
target:set("suffixname", "-d") |
target:set("extension", ".dll") |
target:set("targetdir", "lib") |
target:set("objectdir", "obj") |
target:set("dependir", "deps") |
target:set("rundir", "bin") |
target:set("runargs", "--verbose") |
target:set("installdir", "/usr") |
target:set("prefixdir", "subdir") |
target:set("configdir", "out") |
target:set("group", "mygroup") |
target:set("languages", "cxx20") |
target:set("optimize", "fastest") |
target:set("warnings", "all") |
target:set("symbols", "debug") |
target:set("exceptions", "cxx") |
target:set("runtimes", "MD") |
target:set("fpmodels", "fast") |
target:set("encodings", "utf-8") |
target:set("strip", "all") |
target:set("enabled", true) |
target:set("default", false) |
target:set("toolchains", "clang") |
target:set("toolset", "cc", "/usr/bin/gcc") |
target:set("plat", "linux") |
target:set("arch", "x64") |
target:set("policy", "build.optimization.lto", true) |
Note: For the target:add("name", ...) / target:set("name", ...) pattern, any key name works through xmake's generic values mechanism. Only explicitly defined APIs (like files, deps, kind) have special handling.
4. Compilation Flags (by Language)
These APIs pass compiler-specific flags:
| API | Description |
|---|
add_cflags(...) | C compilation flags |
add_cxflags(...) | C/C++ compilation flags |
add_cxxflags(...) | C++ compilation flags |
add_mflags(...) | ObjC compilation flags |
add_mxflags(...) | ObjC/ObjC++ compilation flags |
add_mxxflags(...) | ObjC++ compilation flags |
add_scflags(...) | Swift compilation flags |
add_asflags(...) | Assembly compilation flags |
add_gcflags(...) | Go compilation flags |
add_dcflags(...) | D language compilation flags |
add_rcflags(...) | Rust compilation flags |
add_fcflags(...) | Fortran compilation flags |
add_zcflags(...) | Zig compilation flags |
add_cuflags(...) | CUDA compilation flags |
add_culdflags(...) | CUDA device link flags |
add_cugencodes(...) | CUDA gencode settings (e.g., "sm_30", "native") |
Linker Flags
| API | Description |
|---|
add_ldflags(...) | Static library/exe link flags |
add_arflags(...) | Archive (static library) flags |
add_shflags(...) | Dynamic library link flags |
Example with per-tool flags:
on_load(function(target)
target:add("cxflags", "-fPIC", {tools = {"clang", "gcc"}, public = true})
target:add("cxflags", "/Zc:preprocessor", {tools = "cl"})
target:add("ldflags", "-Wl,-rpath,.", {force = true, expand = false})
end)
5. Precompiled Headers (PCH)
target("my-target")
set_pcheader("precompiled.h")
set_pcxxheader("precompiled.hpp")
Enable conditionally with:
if has_config("enable_pch") then
set_pcxxheader("mypch.hpp")
end
6. Conditional Configuration with Conditions
on_load(function(target)
if target:is_plat("windows") then
target:add("defines", "NOMINMAX", "PLATFORM_WINDOWS")
target:add("syslinks", "Advapi32", "Ole32")
elseif target:is_plat("linux") then
target:add("syslinks", "dl", "uuid", "pthread")
target:add("cxflags", "-fPIC")
elseif target:is_plat("macosx") then
target:add("frameworks", "CoreFoundation", "Metal")
end
if target:is_arch("x64", "x86_64") then
target:add("vectorexts", "avx2")
elseif target:is_arch("arm64", "aarch64") then
target:add("defines", "PLATFORM_ARM")
end
if is_mode("debug") then
target:set("symbols", "debug")
target:set("optimize", "none")
target:set(, )
is_mode()
target:set(, )
target:set(, )
target:set(, )
has_config()
target:add(, )
target:add(, )
has_package()
target:add(, )
target:get() ==
target:add(, , {public = })
target:get() ==
target:add(, , {public = })
)
Standalone Condition Functions (usable in any scope)
if is_plat("windows") then ... end
if is_arch("x64") then ... end
if is_mode("debug") then ... end
if is_os("windows") then ... end
if is_host("windows") then ... end
if is_subhost("msys") then ... end
if is_subarch(...) then ... end
if is_cross() then ... end
if is_kind("static") then ... end
if is_config("var", "value") then ... end
if has_config("feature") then ...
has_package() ...
7. Lifecycle Hooks
target("my-target")
on_load(function(target)
end)
on_config(function(target)
end)
on_prepare(function(target)
end)
on_prepare_file(func)
on_prepare_files(func)
on_build(function(target)
end)
on_build_file(func)
on_build_files(func)
on_link(function(target)
end)
on_clean(function(target)
end)
on_package(function(target)
end)
on_install(function(target)
end)
on_uninstall(function(target)
end)
on_run(
)
on_test(
)
before_build( ... )
after_build( ... )
before_link( ... )
after_link( ... )
before_install( ... )
after_install( ... )
Common Use of after_build — Copy DLLs
after_build(function(target)
if is_plat("windows") then
os.cp("path/to/mylib.dll", target:targetdir())
elseif is_plat("linux") then
os.cp("path/to/libmylib.so", target:targetdir())
end
end)
8. Visibility and Inheritance
Many target:add() / target:set() calls accept a visibility table to control propagation:
target:add("includedirs", "include", {public = true})
target:add("defines", "PUBLIC_DEF", {public = true})
target:add("links", "mylib", {public = true})
target:add("includedirs", "include", {interface = true})
target:add("defines", "PRIVATE_DEF", {private = true})
Dependency inheritance can be controlled per-target:
add_deps("foo", {inherit = false})
add_deps("bar", {inherit = true})
add_deps("baz", {links = false})
9. Tests
target("my-test")
set_kind("binary")
add_files("test_*.cpp")
add_tests("test_foo", {
runargs = {"--arg1", "--arg2"},
runenvs = {PATH = "/usr/bin"},
timeout = 30,
group = "unit",
pass_outputs = {"PASSED"},
fail_outputs = {"FAILED"},
should_fail = false,
build_should_pass = true,
})
on_test(function(target)
local ok = os.execv("./my_test")
if not ok then
return false, "test failed"
end
return true
end)
10. Common Target Patterns
10.1 Shared Library
target("mylib")
set_kind("shared")
set_basename("mylib")
add_deps("core")
add_headerfiles("include/**.h")
on_load(function(target)
target:add("defines", "MYLIB_EXPORT_DLL")
target:add("includedirs", "include", {public = true})
target:add("files", "src/*.cpp")
if target:is_plat("windows") then
target:add("defines", "NOMINMAX")
target:add("syslinks", "Advapi32")
elseif target:is_plat("macosx") then
target:add("frameworks", "Foundation")
end
if has_config("enable_extra") then
target:add("defines", "EXTRA_FEATURE")
target:add("files", "src/extra/*.cpp")
end
end)
if has_config("enable_pch") then
set_pcxxheader("src/mylib_pch.h")
end
target_end()
10.2 Static Library
target("mystatic")
set_kind("static")
set_basename("mystatic")
add_deps("core")
add_headerfiles("include/**.h")
add_files("src/*.cpp")
add_defines("MYSTATIC_STATIC_LIB", {public = true})
target_end()
10.3 Executable (Binary)
target("my-tool")
set_kind("binary")
add_deps("runtime", "dsl")
add_files("main.cpp")
add_includedirs("include")
on_load(function(target)
if has_config("enable_gui") then
target:add("deps", "gui")
target:add("defines", "ENABLE_GUI")
end
end)
target_end()
10.4 Phony Target (Meta / Validation)
target("my-validator")
set_kind("phony")
add_deps("runtime")
on_config(function(target)
if target:is_plat("windows") then
local toolchain = target:toolchain("msvc")
end
end)
target_end()
10.5 Header-only Target
target("my-headers")
set_kind("headeronly")
add_headerfiles("include/**.h")
add_includedirs("include", {public = true})
target_end()
10.6 Test Target (using a helper function)
local function test_proj(name, source, extra)
target(name)
set_kind("binary")
add_deps("runtime", "dsl")
add_files(source)
add_includedirs("common")
if extra then extra() end
target_end()
end
test_proj("test_foo", "tests/test_foo.cpp")
test_proj("test_bar", "tests/test_bar.cpp", function()
add_defines("EXTRA")
add_deps("extra-dep")
end)
10.7 Object Target (Intermediate objects only)
target("my-objects")
set_kind("object")
add_files("src/*.cpp")
target_end()
11. Custom Rules
add_rules() must be outside on_load:
target("my-target")
add_rules("c.unity_build", {batchsize = 8})
add_rules("c++.unity_build", {batchsize = 8})
add_rules("utils.bin2obj", {extensions = {".cu", ".h"}})
add_rules("build_cargo")
add_rules("lc_llvm")
target_end()
Rules with Custom Values
target("my-target")
add_rules("my-rule")
set_values("mykey", "value1", "value2")
add_values("mykey", "value3")
target_end()
12. The on_load / on_config Target Object
Inside lifecycle hooks, the target object provides these methods:
| Method | Description |
|---|
target:name() | Get target name |
target:fullname() | Get full name (with namespace) |
target:targetdir() | Get output directory |
target:targetfile() | Get target file path |
target:scriptdir() | Get directory of the xmake.lua file |
target:arch() | Get target architecture |
target:plat() | Get target platform |
target:is_plat("windows") | Check platform |
target:is_arch("x64") | Check architecture |
target:is_arch64() | Is 64-bit architecture? |
target:is_mode("debug") | Check build mode (alias for is_mode()) |
target:is_cross() | Is cross-compilation? |
target:has_tool("cxx", "clang") | Check if using specific tool |
target:get("kind") | Get any target property |
target:get_from("links", "*") | Get values from all sources (self, deps, options, packages) |
target:add("key", "value", {public=true}) | Add configuration |
target:set("key", "value") | Override configuration |
target:deps() | Get all dependent targets (after_load only) |
target:dep("name") | Get a specific dependency (after_load only) |
target:orderdeps({inherit=true}) | Get ordered deps |
target:toolchain("msvc") | Get toolchain instance |
target:compiler("cxx") | Get compiler instance |
|
13. Dependencies: Options & Packages
target("my-target")
add_options("my_option")
set_options("my_option")
add_requires("spdlog", "fmt")
target("my-target")
add_packages("spdlog", "fmt")
add_packages("sfml", {components = {"graphics", "window"}})
add_deps("lib-a", "lib-b", {inherit = true})
add_deps("lib-c", {inherit = false})
add_deps("lib-d", {links = false})
14. Run Environment
target("my-target")
set_runenv("PATH", "/custom/path")
add_runenvs("PATH", "/extra/path")
15. File Management
target("my-target")
add_files("src/*.cpp")
add_files("src/*.cpp", {sourcekind = "cxx"})
add_files("src/*.m", {sourcekind = "mxx"})
remove_files("src/old.cpp")
add_headerfiles("include/**.h")
remove_headerfiles("include/deprecated.h")
add_installfiles("config/*.ini")
add_configfiles("config.h.in")
add_extrafiles("README.md")
add_forceincludes("precompiled.h")
16. Complete Example
set_xmakever("3.0.6")
add_rules("mode.release", "mode.debug")
add_requires("spdlog")
target("mylib")
set_kind("shared")
set_basename("mylib")
add_deps("core")
add_headerfiles("include/**.h")
add_rules("c++.unity_build", {batchsize = 8})
if has_config("enable_pch") then
set_pcxxheader("src/mylib_pch.h")
end
on_load(function(target)
target:add("files", "src/*.cpp")
if has_config("enable_extra") then
target:add("files", "src/extra/*.cpp")
target:add("defines", "ENABLE_EXTRA")
end
target:add("includedirs", "include", {public = true})
if target:is_plat("windows") then
target:add("defines", "NOMINMAX", "PLATFORM_WIN", "MYLIB_EXPORT_DLL")
target:add("syslinks", "Advapi32", "Ole32")
target:add("cxflags", , {tools = })
target:is_plat()
target:add(, , )
target:add(, )
target:is_plat()
target:add(, , , )
target:add(, )
target:is_arch(, )
target:add(, )
has_config()
target:add(, )
target:add(, )
)
after_build(
is_plat()
.cp(, target:targetdir())
)
target_end()
target()
set_kind()
add_deps()
add_files()
add_tests(, {
runargs = {},
group = ,
})
on_load(
target:add(, )
)
target_end()
Summary
- Use
on_load for conditional logic — platform checks, feature flags, dynamic file lists.
add_rules() stays outside — cannot be set from inside on_load.
- Simple globs outside, conditional additions inside — keep
add_files/add_headerfiles outside for simple cases.
- Prefer
target:set() / target:add() inside on_load for most configuration — it's equivalent to outside calls.
- Visibility —
{public = true} propagates to dependents, {interface = true} propagates only to dependents, {private = true} (default) is local-only.
add_deps() outside = target:add("deps", ...) inside — choose whichever fits your style.
- All APIs listed here work at the target scope level — use them outside
on_load as set_kind(...) or inside as target:set("kind", ...).---
Lua Scripting in xmake
Reference: D:/xmake/core/sandbox/modules/, D:/xmake/modules/, D:/xmake/core/base/
xmake scripts (in on_load, on_build, after_install, etc.) run in a sandboxed Lua environment. This section documents all available built-in modules and APIs.
1. Built-in Sandbox Modules
1.1 print / printf — Output
print("hello", "world")
printf("hello %s", "world")
vprint("verbose msg")
dprint("diagnosis msg")
1.2 cprint / cprintf — Colored Output
cprint("${bright}hello${reset}")
cprint("${red}error${reset}")
cprint("${color.dump.string}hello")
cprint("${dim}%s${reset}", "world")
Available color tags: ${red}, ${green}, ${blue}, ${yellow}, ${magenta}, ${cyan}, ${bright}, ${dim}, ${reset}, ${underline}, etc.
1.3 utils — Utilities
utils.dump(obj)
utils.assert(value, "msg", ...)
utils.error("err %s", arg)
utils.warning("warn %s", arg)
utils.trycall(func)
1.4 Path Operations
path.join("a", "b", "c")
path.join("a", "..", "b")
path.absolute("rel/path")
path.relative("/abs/path", "/base")
path.basename("foo/bar.cpp")
path.filename("foo/bar.cpp")
path.extension("foo/bar.cpp")
path.directory("foo/bar.cpp")
path.normalize("a/./b/../c")
1.5 string — String Operations
All standard Lua string functions are available. Extended functions:
string.vformat("$(var) hello", ...)
string.format("hello %s", "world")
Built-in variables (resolved in strings via $() or vformat):
| Variable | Description |
|---|
$(host) | Host OS (windows, linux, macosx) |
$(tmpdir) | Temp directory |
$(curdir) | Current directory |
$(scriptdir) | Directory of the current xmake.lua |
$(projectdir) | Project root directory |
$(buildir) | Build output directory |
$(globaldir) | Global xmake directory |
$(programdir) | xmake installation directory |
Example:
path.join("$(projectdir)", "build")
print("$(scriptdir)")
1.6 table — Table Operations
table.join(t1, t2)
table.join2(t1, t2)
table.clone(t)
table.wrap(v)
table.unwrap({v})
table.contains(t, value)
table.unique(t)
table.reverse(t)
table.slice(t, first, last)
table.is_array(t)
table.is_dictionary(t)
table.keys(t)
table.values(t)
table.pack(...)
table.map(t, mapper)
table.imap(t, mapper)
table.find(t, value)
table.find_if(t, pred)
table.remove_if(t, pred)
.empty(t)
.orderkeys(t, callback)
.orderpairs(t, callback)
.inherit(...)
1.7 os — Operating System
File/Directory Operations
os.cp("src/file", "dst/file")
os.mv("src/file", "dst/file")
os.rm("file_or_dir")
os.ln("target", "symlink")
os.mkdir("dir")
os.rmdir("dir")
os.cd("dir")
os.touch("file")
os.isfile("path")
os.isdir("path")
os.islink("path")
os.isexec("path")
os.exists("path")
os.readlink("symlink")
os.filesize("file")
.mtime()
File Matching (Globbing)
os.files("src/*.cpp")
os.dirs("src/*")
os.filedirs("src/*")
os.match("src/*.c", "file")
Running Commands
os.run("gcc -c %s -o %s", "file.c", "file.o")
os.runv("gcc", {"-c", "file.c", "-o", "file.o"})
local out, err = os.iorun("echo hello")
local out, err = os.iorunv("python", {"--version"})
local exitok, errors = os.exec("ls")
local exitok, errors = os.execv("python", {"script.py"})
os.vrun("gcc %s", "file.c")
os.vrunv("gcc", {"-c", "file.c"})
os.vexec("echo hello")
os.vexecv("echo", {"hello"})
os.trycp("src", "dst")
os.trymv("src", "dst")
os.tryrm("file")
Environment Variables
os.getenv("PATH")
os.setenv("MY_VAR", "value")
os.addenv("PATH", "/new/path")
os.getenvs()
os.setenvs({PATH = "/usr/bin"})
os.addenvs({PATH = "/new/path"})
os.joinenvs({PATH = "/a:/b"})
Directory/System Info
os.curdir()
os.scriptdir()
os.projectdir()
os.tmpdir()
os.tmpfile("key")
os.host()
os.arch()
os.subhost()
os.subarch()
os.is_host("windows")
os.is_arch("x64")
os.is_subhost("msys")
os.isroot()
os.fscase()
os.mclock()
os.sleep(1000)
os.nuldev()
os.xmakever()
os.args({, })
.getpid()
.cpuinfo()
.meminfo()
1.8 io — File I/O
local data = io.readfile("path")
io.writefile("path", "content")
local obj = io.load("data.json")
io.save("data.json", obj)
local f = io.open("file.txt", "r")
f:read("*a")
f:read("*l")
f:read(n)
f:write("data")
f:print("format %s", "arg")
f:printf("format %s", "arg")
f:close()
f:flush()
f:seek("set", 0)
f:size()
f:()
f:save(obj)
.(, , )
.replace(, , )
.(, lineidx, )
.cat(, )
.tail(, )
.:()
.:()
.:()
.()
.()
.()
1.9 hash — Hashing
hash.md5("data")
hash.md5("filepath")
hash.sha1("data")
hash.sha256("data")
hash.xxhash32("data")
hash.xxhash64("data")
hash.xxhash128("data")
hash.uuid()
hash.uuid4()
hash.strhash32("str")
hash.strhash64("str")
hash.strhash128("str")
hash.rand32()
hash.rand64()
hash.rand128()
1.10 xmake — xmake Runtime Info
xmake.arch()
xmake.version()
xmake.branch()
xmake.programdir()
xmake.programfile()
xmake.luajit()
xmake.is_embed()
1.11 math — Standard Lua math
1.12 coroutine — Standard Lua coroutine
2. Variable Formatting (vformat)
xmake strings can contain built-in variables resolved with $(var) syntax:
print("$(projectdir)/build")
print("$(scriptdir)/src")
print("$(buildir)/$(mode)")
All os.* functions, io.*, path.* and print functions automatically resolve $(var) in their string arguments.
3. Error Handling: try / catch / finally
local ok = try {
function()
local data = io.readfile("may_not_exist.txt")
if not data then
raise("file not found")
end
return data
end,
catch {
function(errors)
print("caught:", errors)
end
},
finally {
function(ok, result_or_errors)
end
}
}
Short form (no catch):
local ok = try { function() return io.readfile("file") end }
raise — Throw an error
raise("something went wrong")
raise({errors = "msg", stderr = "..."})
assert — from utils
utils.assert(io.readfile("f"), "cannot read file")
4. Module Import System (import)
xmake provides a module system for importing extension modules from the modules/ directory.
import("core.project.depend")
import("lib.detect.find_tool")
import("detect.sdks.find_cuda")
import("core.base.option")
local tool = find_tool("gcc")
local cuda = find_cuda()
local opt = option.get("verbose")
Common extension modules
| Module | Description |
|---|
lib.detect.find_tool | Find a system tool/executable |
lib.detect.find_file | Find a file in search paths |
lib.detect.find_library | Find a library (name + paths) |
lib.detect.find_package | Find a package (pkg-config, builtin detectors) |
lib.detect.find_program | Find a program in PATH |
detect.sdks.find_cuda | Find CUDA SDK |
detect.sdks.find_ndk | Find Android NDK |
detect.packages.find_openssl | Find OpenSSL |
detect.packages.find_zlib | Find zlib |
core.project.config | Access project configuration |
core.project.depend | Dependency/file change tracking |
core.project.option | Access option definitions |
core.base.option | Access command-line options |
core.base.global | Access global configuration |
core.base.task | Run xmake tasks programmatically |
core.ui.* | Terminal UI components |
core.language.language | Language extension registration |
utils.archive.* | Archive (.tar, .zip) extraction |
net.* | Network/HTTP utilities |
devel.git.* | Git operations |
async.runjobs | Parallel job execution |
async.jobgraph | Job dependency graph |
Example:
import("lib.detect.find_tool")
on_load(function(target)
local gcc = find_tool("gcc")
if gcc then
print("found gcc at", gcc.program)
end
end)
find_package — shortcut
local pkg = find_package("openssl", {required = false})
if pkg then
target:add("links", pkg.links)
target:add("linkdirs", pkg.linkdirs)
target:add("includedirs", pkg.includedirs)
end
local packages = find_packages("openssl", "zlib", "curl")
5. Compiler/Detect Libraries
lib.detect.find_tool
import("lib.detect.find_tool")
local tool = find_tool("clang", {version = true})
lib.detect.find_package
import("lib.detect.find_package")
local pkg = find_package("openssl", {
paths = {"/usr/local/opt/openssl"},
required = false,
})
lib.detect.find_file
import("lib.detect.find_file")
local header = find_file("python.h", {"/usr/include", "/usr/local/include"})
6. Build Batch Commands
Inside on_buildcmd_file or rule scripts, you can use batch commands:
on_buildcmd_file(function(target, batchcmds, sourcefile)
batchcmds:show("compiling %s", sourcefile)
batchcmds:vrun("gcc -c %s", sourcefile)
batchcmds:cp("src.txt", "dst.txt")
end)
7. Private Target Data
Store and retrieve arbitrary data on a target:
on_load(function(target)
target:data_set("mykey", {some = "data"})
end)
after_build(function(target)
local data = target:data("mykey")
print(data.some)
end)
8. Getting Configuration
get_config("lc_enable_dsl")
get_config("my_option")
has_config("lc_enable_dsl")
has_package("spdlog")
9. Complete Scripting Example
import("lib.detect.find_tool")
import("core.base.option")
target("my-scripted-target")
set_kind("binary")
add_files("src/*.cpp")
on_load(function(target)
local verbose = option.get("verbose")
if verbose then
print("Building for:", target:plat(), target:arch())
end
local clang = find_tool("clang")
if clang then
print("using clang:", clang.program)
end
if target:is_plat("windows") then
target:add("files", "src/*.win.cpp")
target:add("syslinks", "Advapi32")
end
target:data_set("build_time", os.time())
end)
before_build(function(target)
if not os.exists("src/main.cpp") then
raise("main.cpp not found")
end
)
after_build(
target_file = target:targetfile()
.isfile(target_file)
.cp(target_file, .join(, ))
()
start = target:data()
elapsed = .mclock() - start
(, elapsed, )
)
target_end()
Summary
| Module | Key APIs |
|---|
os | cp, mv, rm, mkdir, run, exec, iorun, files, dirs, isfile, exists, getenv, setenv, host, arch, sleep, cd, scriptdir, projectdir |
io | readfile, writefile, load, save, open, gsub, replace, cat, tail, stdin, stdout, stderr |
path | join, absolute, relative, basename, filename, extension, directory, normalize |
table | join, join2, clone, wrap, unwrap, contains, unique, keys, values, map, find, empty |
string | all Lua standard + vformat, format |
utils | dump, assert, error, warning, trycall |
hash | , , , , , , , |