ast
無料Manual AST construction with FunctionBuilder for kernels and callables without DSL sugar.
日本語の概要は準備中です。原文の説明を表示しています。
XMake build configuration, options, commands, and patterns for LuisaCompute.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Primary build system. Requires XMake 3.0.6+. Optional: CUDA Toolkit, Vulkan SDK, LLVM 20, Rust.
xmake f -m debug -c -y
xmake build
# Update compile_commands.json:
xmake project -k compile_commands --lsp=clangd .vscode
| 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 |
-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.
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.
Configure and build with debug mode as usual:
xmake f -m debug -c -y
xmake build
xmake run <target>
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
| 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 |
-v, -D, --diagnosis invalid; use --verbose--lc_option=true/=false-c to clean cache when reconfiguring with different options-y to auto-accept all prompts and skip interaction — essential in automated scripts and CI pipelineslc_fallback_backend requires both lc_llvm_path and lc_embree_pathlc_dx_backend is silently disabled on non-Windows platformslc_metal_backend is silently disabled on non-macOS platformslc_cuda_backend is silently disabled outside Windows/Linuxhas 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.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.
target("<name>", {kind = "static"}) -- Optional: pass kind inline
-- or
target("<name>")
set_kind("static") -- "static", "shared", "binary", "object", "phony", "headeronly", "moduleonly"
-- Static settings (outside on_load)
set_basename("my-lib") -- Override output filename
add_deps("dep1", "dep2") -- Target dependencies
add_rules("my-rule") -- Custom build rules (MUST be outside on_load)
add_files("src/*.cpp") -- Source files (simple globs outside)
add_headerfiles("include/**.h") -- Header files
on_load(function(target)
-- Dynamic settings (preferred for conditional config)
target:add("includedirs", "include", {public = true})
target:add("defines", "MY_DEFINE", {public = true})
target:add("deps", "another-dep") -- Same as add_deps() outside
target:set("kind", "shared")
target:add("links", "pthread")
target:add("syslinks", "dl")
target:add("packages", "spdlog") -- For xrepo packages
end)
after_build(function(target)
-- Post-build steps (e.g., copy DLLs)
end)
target_end()
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.add_files, add_headerfiles) can go outside; conditional additions go inside on_load.{public = true}, {interface = true}, or {private = true} (default) to control inheritance.Every xmake target has two kinds of declarations:
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, arch, policy |
Rules for config fields:
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.on_load.{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 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:
target argument for target-local config: target:add("field", value), target:set("field", value), target:get("field").is_plat(), is_arch(), is_mode(), has_config(), get_config(), os.*, io.*, path.*, etc.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.false from some hooks (e.g. on_test) signals failure; most hooks ignore return values.target("my-scripted-target")
set_kind("binary")
add_files("src/*.cpp")
add_includedirs("include")
-- Static config field outside
set_basename("myapp")
set_warnings("all")
on_load(function(target)
-- Dynamic config field inside script field
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
-- Record data for later script fields
target:data_set("build_start", os.mclock())
end)
before_build(function(target)
-- Script field code: validate before compiling
local main = path.join(target:scriptdir(), "src/main.cpp")
if not os.isfile(main) then
raise("missing entry point: " .. main)
end
-- Generate a version header
local out = path.join(target:autogendir(), "version.h")
os.mkdir(path.directory(out))
io.writefile(out, string.format("#define BUILD_TIME %d\n", os.time()))
target:add("includedirs", path.directory(out))
end)
after_build(function(target)
-- Script field code: post-build action
local exe = target:targetfile()
if os.isfile(exe) then
local dest = path.join("$(buildir)", "publish")
os.mkdir(dest)
os.cp(exe, dest)
print("published:", exe)
end
local start = target:data("build_start")
if start then
print("elapsed:", os.mclock() - start, "ms")
end
end)
target_end()
on_loadUse 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) |
target:set("options", "opt1") |
target:set("values.mykey", "val") |
target:set("configvar", "VAR", "value") |
target:set("runenv", "PATH", "/usr/bin") |
target:set("pcheader", "header.h") |
target:set("pcxxheader", "header.hpp") |
target:set("pmheader", "header.m") |
target:set("pmxxheader", "header.mm") |
Note: For the
target:add("name", ...)/target:set("name", ...)pattern, any key name works through xmake's generic values mechanism. Only explicitly defined APIs (likefiles,deps,kind) have special handling.
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") |
| 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)
target("my-target")
set_pcheader("precompiled.h") -- C PCH
set_pcxxheader("precompiled.hpp") -- C++ PCH
Enable conditionally with:
if has_config("enable_pch") then
set_pcxxheader("mypch.hpp")
end
on_load(function(target)
-- Platform checks
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
-- Architecture checks
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
-- Build mode
if is_mode("debug") then
target:set("symbols", "debug")
target:set("optimize", "none")
target:set("runtimes", "MDd")
elseif is_mode("release") then
target:set("optimize", "aggressive")
target:set("symbols", "hidden")
target:set("runtimes", "MD")
end
-- Config option checks
if has_config("my_feature") then
target:add("defines", "MY_FEATURE_ENABLED")
target:add("deps", "my-feature-dep")
end
if has_package("spdlog") then
target:add("packages", "spdlog")
end
-- Target kind checks
if target:get("kind") == "static" then
target:add("defines", "MYLIB_STATIC", {public = true})
elseif target:get("kind") == "shared" then
target:add("defines", "MYLIB_EXPORT", {public = true})
end
end)
if is_plat("windows") then ... end -- Current target platform
if is_arch("x64") then ... end -- Current target architecture
if is_mode("debug") then ... end -- Current build mode
if is_os("windows") then ... end -- Target OS (e.g., "ios", "android")
if is_host("windows") then ... end -- Host OS running xmake
if is_subhost("msys") then ... end -- Subsystem (e.g., "msys", "cygwin")
if is_subarch(...) then ... end -- Subsystem architecture
if is_cross() then ... end -- Cross-compilation check
if is_kind("static") then ... end -- Target kind check
if is_config("var", "value") then ... end -- Config option value check
if has_config("feature") then ... end -- Config option exists/enabled?
if has_package("pkg") then ... end -- Package exists/enabled?
target("my-target")
-- Loading phase
on_load(function(target) -- When target is loaded (early)
end)
on_config(function(target) -- After 'xmake config', before build
end)
-- Build preparation
on_prepare(function(target) -- Source preprocessing/code generation
end)
on_prepare_file(func) -- Single file preprocessing
on_prepare_files(func) -- Batch file preprocessing
-- Build phase
on_build(function(target) -- Override entire build
end)
on_build_file(func) -- Replace single file compilation
on_build_files(func) -- Replace batch file compilation
on_link(function(target) -- Custom link process
end)
-- Clean / Package / Install / Run
on_clean(function(target)
end)
on_package(function(target)
end)
on_install(function(target)
end)
on_uninstall(function(target)
end)
on_run(function(target) -- Override 'xmake run'
end)
-- Test hooks
on_test(function(target) -- Custom test (return true=pass)
end)
-- Before/After variants exist for all of the above:
before_build(function(target) ... end)
after_build(function(target) ... end)
before_link(function(target) ... end)
after_link(function(target) ... end)
before_install(function(target) ... end)
after_install(function(target) ... end)
-- ... etc.
after_build — Copy DLLsafter_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)
Many target:add() / target:set() calls accept a visibility table to control propagation:
-- Public: propagated to dependent targets + current target
target:add("includedirs", "include", {public = true})
target:add("defines", "PUBLIC_DEF", {public = true})
target:add("links", "mylib", {public = true})
-- Interface: only propagated to dependents (not current target)
target:add("includedirs", "include", {interface = true})
-- Private: only for current target (default)
target:add("defines", "PRIVATE_DEF", {private = true})
Dependency inheritance can be controlled per-target:
add_deps("foo", {inherit = false}) -- No inheritance from this dep
add_deps("bar", {inherit = true}) -- Default: inherit
add_deps("baz", {links = false}) -- Don't inherit links from this dep
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,
})
-- Or with custom test script
on_test(function(target)
-- Return true for pass, false + error for fail
local ok = os.execv("./my_test")
if not ok then
return false, "test failed"
end
return true
end)
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()
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()
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()
target("my-validator")
set_kind("phony") -- No build output
add_deps("runtime")
on_config(function(target)
if target:is_plat("windows") then
local toolchain = target:toolchain("msvc")
-- Validate SDK version, toolchain, etc.
end
end)
target_end()
target("my-headers")
set_kind("headeronly")
add_headerfiles("include/**.h")
add_includedirs("include", {public = true})
target_end()
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)
target("my-objects")
set_kind("object") -- Compiles sources but does not link
add_files("src/*.cpp")
target_end()
add_rules() must be outside on_load:
target("my-target")
add_rules("c.unity_build", {batchsize = 8}) -- Unity build
add_rules("c++.unity_build", {batchsize = 8})
add_rules("utils.bin2obj", {extensions = {".cu", ".h"}}) -- Binary embedding
add_rules("build_cargo") -- Rust/Cargo build
add_rules("lc_llvm") -- LLVM integration
target_end()
target("my-target")
add_rules("my-rule")
set_values("mykey", "value1", "value2")
add_values("mykey", "value3")
target_end()
on_load / on_config Target ObjectInside 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 |
target:linker() | Get linker instance |
target:sourcebatches() | Get source file batches |
target:objectdir() | Get object directory |
target:dependir() | Get dependency directory |
target:autogendir() | Get auto-generated files directory |
target:data("key") | Get user private data |
target:data_set("key", value) | Set user private data |
target:values("name") | Get custom values |
target:values_set("name", ...) | Set custom values |
target:rule("name") | Get a rule instance |
target:rule_enable("name", bool) | Enable/disable a rule |
target:extraconf("name", "item", "key") | Get extra configuration |
target:extraconf_from("name", "source") | Get extra config from source |
target:pkgs() | Get all packages |
target:pkg("name") | Get a package instance |
target:is_kind("kind") | Check target kind |
target:kind() | Get target kind |
target:basename() | Get output base name |
target:filename() | Get output filename |
target:version() | Get target version |
target:clone() | Clone the target (after_load only) |
target:is_phony() | Is phony target? |
target:is_binary() | Is binary target? |
target:is_shared() | Is shared library? |
target:is_static() | Is static library? |
target:is_library() | Is any library type? |
target:is_enabled() | Is target enabled? |
target:is_default() | Is default build target? |
target:is_rebuilt() | Was target rebuilt? |
target("my-target")
-- Option dependencies
add_options("my_option")
set_options("my_option")
-- Package dependencies (requires add_requires in root scope)
add_requires("spdlog", "fmt")
target("my-target")
add_packages("spdlog", "fmt")
-- With component selection
add_packages("sfml", {components = {"graphics", "window"}})
-- Internal target dependencies with fine-grained control
add_deps("lib-a", "lib-b", {inherit = true}) -- Full inheritance
add_deps("lib-c", {inherit = false}) -- No inheritance
add_deps("lib-d", {links = false}) -- Don't inherit links
target("my-target")
set_runenv("PATH", "/custom/path") -- Override environment variable
add_runenvs("PATH", "/extra/path") -- Append to environment variable
target("my-target")
add_files("src/*.cpp")
add_files("src/*.cpp", {sourcekind = "cxx"}) -- With per-file options
add_files("src/*.m", {sourcekind = "mxx"}) -- ObjC++ files
remove_files("src/old.cpp") -- Remove previously added files
add_headerfiles("include/**.h")
remove_headerfiles("include/deprecated.h")
add_installfiles("config/*.ini")
add_configfiles("config.h.in") -- Template config files with @var@ substitution
add_extrafiles("README.md") -- Extra files for IDE listing
add_forceincludes("precompiled.h") -- Force-include header
-- Root xmake.lua
set_xmakever("3.0.6")
add_rules("mode.release", "mode.debug")
add_requires("spdlog")
-- Library target
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)
-- Source files
target:add("files", "src/*.cpp")
if has_config("enable_extra") then
target:add("files", "src/extra/*.cpp")
target:add("defines", "ENABLE_EXTRA")
end
-- Public include dirs and defines
target:add("includedirs", "include", {public = true})
-- Platform config
if target:is_plat("windows") then
target:add("defines", "NOMINMAX", "PLATFORM_WIN", "MYLIB_EXPORT_DLL")
target:add("syslinks", "Advapi32", "Ole32")
target:add("cxflags", "/Zc:preprocessor", {tools = "cl"})
elseif target:is_plat("macosx") then
target:add("frameworks", "Foundation", "Metal")
target:add("cxflags", "-fobjc-arc")
elseif target:is_plat("linux") then
target:add("syslinks", "dl", "pthread", "uuid")
target:add("cxflags", "-fPIC")
end
-- Arch config
if target:is_arch("x64", "x86_64") then
target:add("vectorexts", "avx2")
end
-- Package dependencies
if has_config("use_xrepo_spdlog") then
target:add("packages", "spdlog")
else
target:add("deps", "spdlog-bundled")
end
end)
after_build(function(target)
if is_plat("windows") then
os.cp("$(buildir)/mydep.dll", target:targetdir())
end
end)
target_end()
-- Test executable
target("test-mylib")
set_kind("binary")
add_deps("mylib")
add_files("tests/*.cpp")
add_tests("test_basic", {
runargs = {"--verbose"},
group = "unit",
})
on_load(function(target)
target:add("includedirs", "tests")
end)
target_end()
on_load for conditional logic — platform checks, feature flags, dynamic file lists.add_rules() stays outside — cannot be set from inside on_load.add_files/add_headerfiles outside for simple cases.target:set() / target:add() inside on_load for most configuration — it's equivalent to outside calls.{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.on_load as set_kind(...) or inside as target:set("kind", ...).---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.
print / printf — Outputprint("hello", "world") -- Print with newline
printf("hello %s", "world") -- Print without newline
vprint("verbose msg") -- Only printed with -v/--verbose
dprint("diagnosis msg") -- Only printed with --diagnosis
cprint / cprintf — Colored Outputcprint("${bright}hello${reset}") -- Bright text
cprint("${red}error${reset}") -- Red text
cprint("${color.dump.string}hello") -- Dump color
cprint("${dim}%s${reset}", "world") -- Dim text
Available color tags: ${red}, ${green}, ${blue}, ${yellow}, ${magenta}, ${cyan}, ${bright}, ${dim}, ${reset}, ${underline}, etc.
utils — Utilitiesutils.dump(obj) -- Dump object for debugging
utils.assert(value, "msg", ...) -- Assert with error message
utils.error("err %s", arg) -- Error message
utils.warning("warn %s", arg) -- Warning message
utils.trycall(func) -- Call function safely (returns ok, ...)
path.join("a", "b", "c") -- "a/b/c" (OS-aware)
path.join("a", "..", "b") -- "b"
path.absolute("rel/path") -- Full absolute path
path.relative("/abs/path", "/base") -- Relative path from base
path.basename("foo/bar.cpp") -- "bar.cpp"
path.filename("foo/bar.cpp") -- "bar"
path.extension("foo/bar.cpp") -- ".cpp"
path.directory("foo/bar.cpp") -- "foo"
path.normalize("a/./b/../c") -- "a/c"
string — String OperationsAll standard Lua string functions are available. Extended functions:
string.vformat("$(var) hello", ...) -- Format with built-in variables
string.format("hello %s", "world") -- Standard Lua format
-- All standard: sub, gsub, find, match, gmatch, upper, lower, rep, reverse, char, byte, len, split
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") -- Resolves to /path/to/project/build
print("$(scriptdir)") -- Prints script directory
table — Table Operationstable.join(t1, t2) -- Merge tables (new table)
table.join2(t1, t2) -- Merge into t1 (in-place)
table.clone(t) -- Deep clone
table.wrap(v) -- Wrap single value as table {v}
table.unwrap({v}) -- Unwrap table to single value
table.contains(t, value) -- Check if value exists
table.unique(t) -- Remove duplicates
table.reverse(t) -- Reverse array
table.slice(t, first, last) -- Slice array
table.is_array(t) -- Is array-like?
table.is_dictionary(t) -- Is dict-like?
table.keys(t) -- Get keys array
table.values(t) -- Get values array
table.pack(...) -- Pack arguments (like {...} but with .n)
table.map(t, mapper) -- Map values
table.imap(t, mapper) -- In-place map
table.find(t, value) -- Find index of value
table.find_if(t, pred) -- Find if predicate matches
table.remove_if(t, pred) -- Remove if predicate matches
table.empty(t) -- Is empty?
table.orderkeys(t, callback) -- Ordered keys
table.orderpairs(t, callback) -- Ordered pairs iterator
table.inherit(...) -- Prototype-based inheritance
os — Operating Systemos.cp("src/file", "dst/file") -- Copy file/dir
os.mv("src/file", "dst/file") -- Move file/dir
os.rm("file_or_dir") -- Remove file/dir
os.ln("target", "symlink") -- Create symlink
os.mkdir("dir") -- Create directory
os.rmdir("dir") -- Remove directory
os.cd("dir") -- Change directory (returns old cwd)
os.touch("file") -- Touch file
os.isfile("path") -- Is file?
os.isdir("path") -- Is directory?
os.islink("path") -- Is symlink?
os.isexec("path") -- Is executable?
os.exists("path") -- Exists?
os.readlink("symlink") -- Read symlink target
os.filesize("file") -- File size
os.mtime("file") -- Modification time
os.files("src/*.cpp") -- Match .cpp files
os.dirs("src/*") -- Match directories
os.filedirs("src/*") -- Match files and dirs
os.match("src/*.c", "file") -- Match with mode ("file", "dir", "alldir")
-- Run command, raise on failure
os.run("gcc -c %s -o %s", "file.c", "file.o")
os.runv("gcc", {"-c", "file.c", "-o", "file.o"})
-- Run command, return output
local out, err = os.iorun("echo hello")
local out, err = os.iorunv("python", {"--version"})
-- Run command, capture exit code
local exitok, errors = os.exec("ls")
local exitok, errors = os.execv("python", {"script.py"})
-- Verbose variants (print command if -v enabled)
os.vrun("gcc %s", "file.c")
os.vrunv("gcc", {"-c", "file.c"})
os.vexec("echo hello")
os.vexecv("echo", {"hello"})
-- Try variants (no raise on failure)
os.trycp("src", "dst")
os.trymv("src", "dst")
os.tryrm("file")
os.getenv("PATH") -- Get env var
os.setenv("MY_VAR", "value") -- Set env var (override)
os.addenv("PATH", "/new/path") -- Append to env var
os.getenvs() -- Get all env vars
os.setenvs({PATH = "/usr/bin"}) -- Set multiple env vars
os.addenvs({PATH = "/new/path"}) -- Append multiple env vars
os.joinenvs({PATH = "/a:/b"}) -- Join env values
os.curdir() -- Current directory
os.scriptdir() -- Directory of current xmake.lua
os.projectdir() -- Project root directory
os.tmpdir() -- System temp directory
os.tmpfile("key") -- Generate temp file path
os.host() -- Host OS name
os.arch() -- Host architecture
os.subhost() -- Subsystem host
os.subarch() -- Subsystem arch
os.is_host("windows") -- Check host OS
os.is_arch("x64") -- Check host arch
os.is_subhost("msys") -- Check subsystem
os.isroot() -- Is running as root?
os.fscase() -- Is filesystem case-sensitive?
os.mclock() -- CPU clock (ms)
os.sleep(1000) -- Sleep ms (coroutine-safe)
os.nuldev() -- Null device path
os.xmakever() -- xmake version (semver)
os.args({"-a", "-b"}) -- Format args array to string
os.getpid() -- Current process ID
os.cpuinfo() -- CPU info table
os.meminfo() -- Memory info table
io — File I/O-- Read/write entire files
local data = io.readfile("path") -- Read all text
io.writefile("path", "content") -- Write text
local obj = io.load("data.json") -- Load serialized object (JSON/Lua)
io.save("data.json", obj) -- Save serialized object
-- Open file handle
local f = io.open("file.txt", "r") -- "r" read, "w" write, "a" append
f:read("*a") -- Read all
f:read("*l") -- Read line
f:read(n) -- Read n bytes
f:write("data") -- Write
f:print("format %s", "arg") -- Write formatted with newline
f:printf("format %s", "arg") -- Write formatted without newline
f:close() -- Close
f:flush() -- Flush
f:seek("set", 0) -- Seek
f:size() -- File size
f:load() -- Load serialized object from file
f:save(obj) -- Save serialized object to file
-- Text replacement
io.gsub("file", "pattern", "replace") -- Global replace in file
io.replace("file", "pattern", "rep") -- Replace occurrences
io.insert("file", lineidx, "text") -- Insert text at line
-- Cat/Tail
io.cat("file", 10) -- Print first 10 lines
io.tail("file", 10) -- Print last 10 lines
-- stdin/stdout/stderr
io.stdin:read("*l") -- Read from stdin
io.stdout:write("hello") -- Write to stdout
io.stderr:write("error") -- Write to stderr
io.write("hello") -- Shortcut for stdout:write
io.print("hello") -- Write string to file
io.flush() -- Flush stdout
hash — Hashinghash.md5("data") -- MD5 hash
hash.md5("filepath") -- MD5 of file
hash.sha1("data") -- SHA1
hash.sha256("data") -- SHA256
hash.xxhash32("data") -- xxHash32
hash.xxhash64("data") -- xxHash64
hash.xxhash128("data") -- xxHash128
hash.uuid() -- Generate UUID v1
hash.uuid4() -- Generate UUID v4
hash.strhash32("str") -- String hash32
hash.strhash64("str") -- String hash64
hash.strhash128("str") -- String hash128
hash.rand32() -- Random 32-bit
hash.rand64() -- Random 64-bit
hash.rand128() -- Random 128-bit
xmake — xmake Runtime Infoxmake.arch() -- xmake architecture
xmake.version() -- xmake version string
xmake.branch() -- xmake git branch
xmake.programdir() -- xmake installation dir
xmake.programfile() -- xmake executable path
xmake.luajit() -- Is running on LuaJIT?
xmake.is_embed() -- Is embedded xmake?
math — Standard Lua mathcoroutine — Standard Lua coroutinevformat)xmake strings can contain built-in variables resolved with $(var) syntax:
print("$(projectdir)/build") -- Resolves to /path/to/project/build
print("$(scriptdir)/src") -- Resolves to /path/to/xmake.lua/src
print("$(buildir)/$(mode)") -- Resolves to build/debug etc.
All os.* functions, io.*, path.* and print functions automatically resolve $(var) in their string arguments.
try / catch / finallylocal ok = try {
function()
-- Risky operation
local data = io.readfile("may_not_exist.txt")
if not data then
raise("file not found")
end
return data
end,
catch {
function(errors)
-- Handle error
print("caught:", errors)
-- errors is the error message string,
-- or a table {errors=..., stderr=..., stdout=...}
end
},
finally {
function(ok, result_or_errors)
-- Always runs (like Lua's __gc)
end
}
}
-- If try succeeds, returns the try function's return values
-- If catch is provided, errors are caught and execution continues
Short form (no catch):
local ok = try { function() return io.readfile("file") end }
raise — Throw an errorraise("something went wrong") -- String error
raise({errors = "msg", stderr = "..."}) -- Table error (for command failures)
assert — from utilsutils.assert(io.readfile("f"), "cannot read file")
-- Raises if first argument is falsy
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")
-- Then use the module
local tool = find_tool("gcc")
local cuda = find_cuda()
local opt = option.get("verbose")
| 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-- Find a single package
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
-- Find multiple packages
local packages = find_packages("openssl", "zlib", "curl")
lib.detect.find_toolimport("lib.detect.find_tool")
local tool = find_tool("clang", {version = true})
-- Returns: {program = "/usr/bin/clang", version = "15.0.0"}
lib.detect.find_packageimport("lib.detect.find_package")
local pkg = find_package("openssl", {
paths = {"/usr/local/opt/openssl"},
required = false, -- Don't error if not found
})
-- Returns: {links = {"ssl", "crypto"}, linkdirs = {...}, includedirs = {...}}
lib.detect.find_fileimport("lib.detect.find_file")
local header = find_file("python.h", {"/usr/include", "/usr/local/include"})
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)
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) -- "data"
end)
get_config("lc_enable_dsl") -- Get config option value
get_config("my_option") -- Get any config value
has_config("lc_enable_dsl") -- Boolean check
has_package("spdlog") -- Check if package is available
import("lib.detect.find_tool")
import("core.base.option")
target("my-scripted-target")
set_kind("binary")
add_files("src/*.cpp")
on_load(function(target)
-- Print build info
local verbose = option.get("verbose")
if verbose then
print("Building for:", target:plat(), target:arch())
end
-- Find a tool
local clang = find_tool("clang")
if clang then
print("using clang:", clang.program)
end
-- Dynamic files based on platform
if target:is_plat("windows") then
target:add("files", "src/*.win.cpp")
target:add("syslinks", "Advapi32")
end
-- Store data for later use
target:data_set("build_time", os.time())
end)
before_build(function(target)
-- Pre-build validation
if not os.exists("src/main.cpp") then
raise("main.cpp not found")
end
end)
after_build(function(target)
-- Post-build: copy output
local target_file = target:targetfile()
if os.isfile(target_file) then
os.cp(target_file, path.join("$(projectdir)", "dist"))
print("copied to dist/")
end
-- Report build time
local start = target:data("build_time")
local elapsed = os.mclock() - start
print("build completed in", elapsed, "ms")
end)
target_end()
| 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 | md5, sha1, sha256, uuid, uuid4, rand32, rand64, xxhash32/64/128 |
xmake | version, arch, branch, programdir, luajit |
print | print, printf, cprint, cprintf, vprint, dprint |
| try/catch | try, catch, finally, raise |
import | Load extension modules from modules/ |
find_package | Find system packages (pkg-config, builtin detectors) |
find_packages | Find multiple packages at once |
get_config / has_config | Access configuration options |
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Manual AST construction with FunctionBuilder for kernels and callables without DSL sugar.
日本語の概要は準備中です。原文の説明を表示しています。
Backend plugin architecture, DeviceInterface, dynamic loading, and command encoding.
日本語の概要は準備中です。原文の説明を表示しています。
CMake build options, custom functions, and backend patterns for LuisaCompute.
日本語の概要は準備中です。原文の説明を表示しています。
C++ naming, formatting, static analysis, and RTTI rules for LuisaCompute.
日本語の概要は準備中です。原文の説明を表示しています。
Debug crashes and test failures via stack-traces, host/device logging, and DSL buffer inspection.
日本語の概要は準備中です。原文の説明を表示しています。
Show uncommitted changes and commit history via git. Use when: (1) inspecting what changed in a file before committing, (2) checking diff of tracked/new/deleted files, (3) reviewing a specific commit's changes, (4) listing files changed in a commit.
日本語の概要は準備中です。原文の説明を表示しています。