Custom Rules
Xmake not only natively supports multi-language file building, but also allows users to implement complex unknown file building through custom build rules. Custom rules let you define specialized build logic for specific file types.
Basic Concepts
Custom build rules are defined using the rule()
function and associate a set of file extensions to rules through set_extensions()
. Once these extensions are associated with rules, calls to add_files()
will automatically use this custom rule.
Creating Simple Rules
Basic Syntax
rule("rulename") set_extensions(".ext1", ".ext2") on_build_file(function (target, sourcefile, opt) -- build logic end)
Example: Markdown to HTML
-- Define a build rule for markdown files rule("markdown") set_extensions(".md", ".markdown") on_build_file(function (target, sourcefile, opt) import("core.project.depend") -- make sure build directory exists os.mkdir(target:targetdir()) -- replace .md with .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- only rebuild if file has changed depend.on_changed(function () -- call pandoc to convert markdown to html os.vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) end, {files = sourcefile}) end) target("test") set_kind("object") add_rules("markdown") add_files("src/*.md")
Applying Rules to Targets
Method 1: Using add_rules()
target("test") set_kind("binary") add_rules("markdown") -- apply markdown rule add_files("src/*.md") -- automatically use markdown rule
Method 2: Specifying in add_files
target("test") set_kind("binary") add_files("src/*.md", {rules = "markdown"}) -- specify rule for specific files
Note
Rules specified via add_files("*.md", {rules = "markdown"})
have higher priority than rules set via add_rules("markdown")
.
Rule Lifecycle
Custom rules support the complete build lifecycle and can execute custom logic at different stages:
Main Stages
- on_load: Executed when rule is loaded
- on_config: Executed after configuration is complete
- before_build: Executed before building
- on_build: Executed during building (overrides default build behavior)
- after_build: Executed after building
- on_clean: Executed during cleaning
- on_package: Executed during packaging
- on_install: Executed during installation
Example: Complete Lifecycle
rule("custom") set_extensions(".custom") on_load(function (target) -- configuration when rule is loaded target:add("defines", "CUSTOM_RULE") end) before_build(function (target) -- preparation work before building print("Preparing to build custom files...") end) on_build_file(function (target, sourcefile, opt) -- process individual source files print("Building file:", sourcefile) end) after_build(function (target) -- cleanup work after building print("Custom build completed") end)
File Processing Methods
Single File Processing (on_build_file)
rule("single") set_extensions(".single") on_build_file(function (target, sourcefile, opt) -- process single file local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".out") os.cp(sourcefile, targetfile) end)
Batch File Processing (on_build_files)
rule("batch") set_extensions(".batch") on_build_files(function (target, sourcebatch, opt) -- batch process multiple files for _, sourcefile in ipairs(sourcebatch.sourcefiles) do print("Processing file:", sourcefile) end end)
Batch Command Mode
Using on_buildcmd_file
and on_buildcmd_files
can generate batch commands instead of directly executing builds:
rule("markdown") set_extensions(".md", ".markdown") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) -- ensure build directory exists batchcmds:mkdir(target:targetdir()) -- generate target file path local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- add pandoc command batchcmds:vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) -- add dependency files batchcmds:add_depfiles(sourcefile) end)
Rule Dependencies
Adding Rule Dependencies
rule("foo") add_deps("bar") -- foo depends on bar rule rule("bar") set_extensions(".bar") on_build_file(function (target, sourcefile, opt) -- bar rule build logic end)
Controlling Execution Order
rule("foo") add_deps("bar", {order = true}) -- ensure bar executes before foo on_build_file(function (target, sourcefile, opt) -- foo rule build logic end)
Common Interfaces
Setting File Extensions
rule("myrule") set_extensions(".ext1", ".ext2", ".ext3")
Adding Import Modules
rule("myrule") add_imports("core.project.depend", "utils.progress") on_build_file(function (target, sourcefile, opt) -- can directly use depend and progress modules end)
Getting Build Information
rule("myrule") on_build_file(function (target, sourcefile, opt) print("Target name:", target:name()) print("Source file:", sourcefile) print("Build progress:", opt.progress) print("Target directory:", target:targetdir()) end)
Practical Examples
Example 1: Resource File Processing
rule("resource") set_extensions(".rc", ".res") on_build_file(function (target, sourcefile, opt) import("core.project.depend") local targetfile = target:objectfile(sourcefile) depend.on_changed(function () os.vrunv("windres", {sourcefile, "-o", targetfile}) end, {files = sourcefile}) end)
Example 2: Protocol Buffer Compilation
rule("protobuf") set_extensions(".proto") on_build_file(function (target, sourcefile, opt) import("core.project.depend") local targetfile = path.join(target:autogendir(), path.basename(sourcefile) .. ".pb.cc") depend.on_changed(function () os.vrunv("protoc", {"--cpp_out=" .. target:autogendir(), sourcefile}) end, {files = sourcefile}) -- add generated file to target target:add("files", targetfile) end)
Best Practices
- Use Dependency Checking: Avoid unnecessary rebuilds through
depend.on_changed()
- Error Handling: Add appropriate error handling logic in rules
- Progress Display: Use
opt.progress
to display build progress - Modularization: Break complex rules into multiple simple rules
- Documentation: Add clear comments and documentation for custom rules
More Information
- Complete API documentation: Custom Rule API
- Built-in rules reference: Built-in Rules
- Rule examples: Rule Examples