summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-04-01 18:02:50 +0800
committerGitHub <[email protected]>2026-04-01 18:02:50 +0800
commit75b084fa57f2fe00f369d9cac3878c03da43a68a (patch)
tree8ea62a9f04b88e9ae72a59a2fd58d1f7b976797e
parentd7fcddd12197d427ad89d8967515f2e7afd16790 (diff)
parent57ef7e0332355a091abe1ee9eefe2a359aeb6f1a (diff)
Merge pull request #7443 from xmake-io/replace
add utils.replace rules
-rw-r--r--tests/apis/add_files_replace/src/config.h2
-rw-r--r--tests/apis/add_files_replace/src/main.c9
-rw-r--r--tests/apis/add_files_replace/test.lua3
-rw-r--r--tests/apis/add_files_replace/xmake.lua16
-rw-r--r--xmake/rules/utils/replace/xmake.lua111
5 files changed, 141 insertions, 0 deletions
diff --git a/tests/apis/add_files_replace/src/config.h b/tests/apis/add_files_replace/src/config.h
new file mode 100644
index 000000000..96d83f651
--- /dev/null
+++ b/tests/apis/add_files_replace/src/config.h
@@ -0,0 +1,2 @@
+#pragma once
+#define VERSION "1.0"
diff --git a/tests/apis/add_files_replace/src/main.c b/tests/apis/add_files_replace/src/main.c
new file mode 100644
index 000000000..d2ac8f8ae
--- /dev/null
+++ b/tests/apis/add_files_replace/src/main.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include "config.h"
+
+#define HELLO "original"
+
+int main(int argc, char** argv) {
+ printf("hello %s, %s!\n", HELLO, VERSION);
+ return 0;
+}
diff --git a/tests/apis/add_files_replace/test.lua b/tests/apis/add_files_replace/test.lua
new file mode 100644
index 000000000..b57362078
--- /dev/null
+++ b/tests/apis/add_files_replace/test.lua
@@ -0,0 +1,3 @@
+function main(t)
+ t:build()
+end
diff --git a/tests/apis/add_files_replace/xmake.lua b/tests/apis/add_files_replace/xmake.lua
new file mode 100644
index 000000000..ee890c9f8
--- /dev/null
+++ b/tests/apis/add_files_replace/xmake.lua
@@ -0,0 +1,16 @@
+add_rules("mode.debug", "mode.release")
+
+-- test table replaces
+target("test")
+ set_kind("binary")
+ add_files("src/main.c", {rules = "utils.replace", replaces = {
+ {'"original"', '"replaced"'},
+ }})
+
+-- test function replaces
+target("test2")
+ set_kind("binary")
+ add_files("src/main.c", {rules = "utils.replace", replaces = function (content)
+ content = content:gsub('"original"', '"func_replaced"')
+ return content
+ end})
diff --git a/xmake/rules/utils/replace/xmake.lua b/xmake/rules/utils/replace/xmake.lua
new file mode 100644
index 000000000..8b865b750
--- /dev/null
+++ b/xmake/rules/utils/replace/xmake.lua
@@ -0,0 +1,111 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file xmake.lua
+--
+
+-- @usage
+--
+-- e.g. replace with lua pattern (default)
+--
+-- add_files("src/foo.c", {rules = "utils.replace", replaces = {{"RDKCONST%);", "VDKREG);"}}})
+--
+-- e.g. replace with plain text
+--
+-- add_files("src/foo.c", {rules = "utils.replace", replaces = {{"RDKCONST);", "VDKREG);"}}, replace_plain = true})
+--
+-- e.g. replace with custom function
+--
+-- add_files("src/foo.c", {rules = "utils.replace", replaces = function (content)
+-- content = content:gsub("old", "new")
+-- return content
+-- end})
+--
+rule("utils.replace")
+ on_prepare_file(function (target, sourcefile, opt)
+ import("utils.progress")
+ import("core.base.option")
+ import("core.project.depend")
+
+ -- get replace config from fileconfig
+ local fileconfig = target:fileconfig(sourcefile)
+ if not fileconfig or not fileconfig.replaces then
+ return
+ end
+ local replaces = fileconfig.replaces
+ local use_plain = fileconfig.replace_plain
+
+ -- get the replaced file path
+ local replacefile = target:autogenfile(sourcefile)
+
+ -- add the original source directory as includedir,
+ -- so that relative #include paths still work after replacing
+ local sourcedir = path.directory(path.absolute(sourcefile))
+ target:add("includedirs", sourcedir)
+
+ -- replace the sourcefile in the build sourcebatch,
+ -- so that the compiler uses the replaced file instead of the original
+ for _, sourcebatch in pairs(target:sourcebatches()) do
+ if sourcebatch.rulename ~= "utils.replace" then
+ for i, sf in ipairs(sourcebatch.sourcefiles) do
+ if sf == sourcefile then
+ sourcebatch.sourcefiles[i] = replacefile
+ end
+ end
+ end
+ end
+
+ -- build values list from replace config for change detection
+ local depvalues = {}
+ local is_function = type(replaces) == "function"
+ if not is_function then
+ for _, item in ipairs(replaces) do
+ table.insert(depvalues, item[1])
+ table.insert(depvalues, item[2])
+ end
+ if use_plain then
+ table.insert(depvalues, "plain")
+ end
+ end
+
+ -- do replace if source file or replace config is changed
+ depend.on_changed(function ()
+ progress.show(opt.progress, "${color.build.object}replacing.$(mode) %s", sourcefile)
+ vprint("replace %s to %s", sourcefile, replacefile)
+ local content = io.readfile(sourcefile)
+ if is_function then
+ content = replaces(content)
+ else
+ if option.get("diagnosis") then
+ for _, item in ipairs(replaces) do
+ cprint("${dim} > \"%s\" -> \"%s\"%s", item[1], item[2], use_plain and " (plain)" or "")
+ end
+ end
+ for _, item in ipairs(replaces) do
+ if use_plain then
+ content = content:replace(item[1], item[2], {plain = true})
+ else
+ content = content:gsub(item[1], item[2])
+ end
+ end
+ end
+ io.writefile(replacefile, content)
+ end, {dependfile = target:dependfile(replacefile),
+ files = {sourcefile},
+ values = depvalues,
+ changed = target:is_rebuilt()})
+ end)