summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/actions/addon/custom-include/addon.lua2
-rw-r--r--tests/actions/addon/custom-include/includes/check/xmake.lua4
-rw-r--r--tests/actions/addon/custom-module/addon.lua2
-rw-r--r--tests/actions/addon/custom-module/modules/greeting.lua3
-rw-r--r--tests/actions/addon/custom-override/addon.lua2
-rw-r--r--tests/actions/addon/custom-override/plugins/format/main.lua3
-rw-r--r--tests/actions/addon/custom-override/plugins/format/xmake.lua6
-rw-r--r--tests/actions/addon/custom-package/addon.lua2
-rw-r--r--tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h6
-rw-r--r--tests/actions/addon/custom-package/includes/packages/xmake.lua13
-rw-r--r--tests/actions/addon/custom-plugin/addon.lua2
-rw-r--r--tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua5
-rw-r--r--tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua5
-rw-r--r--tests/actions/addon/custom-rule/addon.lua2
-rw-r--r--tests/actions/addon/custom-rule/modules/greeting.lua3
-rw-r--r--tests/actions/addon/custom-rule/rules/hello/xmake.lua6
-rw-r--r--tests/actions/addon/custom-template/addon.lua2
-rw-r--r--tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c6
-rw-r--r--tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua5
-rw-r--r--tests/actions/addon/custom-toolchain/addon.lua6
-rw-r--r--tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua12
-rw-r--r--tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua15
-rw-r--r--tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua19
-rw-r--r--tests/actions/addon/projects/autofetch-badname/xmake.lua5
-rw-r--r--tests/actions/addon/projects/autofetch-build/src/main.c14
-rw-r--r--tests/actions/addon/projects/autofetch-build/xmake.lua14
-rw-r--r--tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock9
-rw-r--r--tests/actions/addon/projects/autofetch-lockmiss/xmake.lua4
-rw-r--r--tests/actions/addon/projects/autofetch/xmake.lua8
-rw-r--r--tests/actions/addon/projects/custom-package/src/main.c6
-rw-r--r--tests/actions/addon/projects/custom-package/xmake.lua9
-rw-r--r--tests/actions/addon/test.lua507
-rw-r--r--tests/modules/binutils/test.lua124
-rw-r--r--tests/modules/project_depend/test.lua19
-rwxr-xr-xtests/modules/semver/test.lua92
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp5
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp7
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp5
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/test.lua107
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua6
-rw-r--r--tests/projects/c++/modules/stdmodules_deps/src/bar.mpp10
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp5
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp3
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp8
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/test.lua1
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua19
-rw-r--r--tests/projects/c++/modules/test_base.lua12
-rw-r--r--tests/projects/embed/esp32/blink/boards/esp32c3/board.h7
-rw-r--r--tests/projects/embed/esp32/blink/src/main.c26
-rw-r--r--tests/projects/embed/esp32/blink/xmake-addons.lua2
-rw-r--r--tests/projects/embed/esp32/blink/xmake.lua15
-rw-r--r--tests/projects/other/bin2c/src/asset.bin1
-rw-r--r--tests/projects/other/bin2c/src/asset2.bin1
-rw-r--r--tests/projects/other/bin2c/src/main.c29
-rw-r--r--tests/projects/other/bin2c/test.lua2
-rw-r--r--tests/projects/other/bin2c/transform.lua5
-rw-r--r--tests/projects/other/bin2c/xmake.lua10
-rw-r--r--tests/projects/other/bin2obj/src/asset.bin1
-rw-r--r--tests/projects/other/bin2obj/src/asset2.bin1
-rw-r--r--tests/projects/other/bin2obj/src/main.c50
-rw-r--r--tests/projects/other/bin2obj/test.lua2
-rw-r--r--tests/projects/other/bin2obj/transform.lua5
-rw-r--r--tests/projects/other/bin2obj/xmake.lua8
63 files changed, 1276 insertions, 19 deletions
diff --git a/tests/actions/addon/custom-include/addon.lua b/tests/actions/addon/custom-include/addon.lua
new file mode 100644
index 000000000..71d01b5c2
--- /dev/null
+++ b/tests/actions/addon/custom-include/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-include")
+ set_description("the addon which provides a custom includes file")
diff --git a/tests/actions/addon/custom-include/includes/check/xmake.lua b/tests/actions/addon/custom-include/includes/check/xmake.lua
new file mode 100644
index 000000000..953f7ba73
--- /dev/null
+++ b/tests/actions/addon/custom-include/includes/check/xmake.lua
@@ -0,0 +1,4 @@
+option("myoption")
+ set_default(true)
+ set_description("The option of the custom-include addon.")
+print("custom-include: includes check is loaded")
diff --git a/tests/actions/addon/custom-module/addon.lua b/tests/actions/addon/custom-module/addon.lua
new file mode 100644
index 000000000..604dbc645
--- /dev/null
+++ b/tests/actions/addon/custom-module/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-module")
+ set_description("the addon which provides a custom module")
diff --git a/tests/actions/addon/custom-module/modules/greeting.lua b/tests/actions/addon/custom-module/modules/greeting.lua
new file mode 100644
index 000000000..843dddfc2
--- /dev/null
+++ b/tests/actions/addon/custom-module/modules/greeting.lua
@@ -0,0 +1,3 @@
+function main(name)
+ return "hello from custom-module: " .. name
+end
diff --git a/tests/actions/addon/custom-override/addon.lua b/tests/actions/addon/custom-override/addon.lua
new file mode 100644
index 000000000..3540b51f5
--- /dev/null
+++ b/tests/actions/addon/custom-override/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-override")
+ set_description("the addon which takes over a builtin plugin")
diff --git a/tests/actions/addon/custom-override/plugins/format/main.lua b/tests/actions/addon/custom-override/plugins/format/main.lua
new file mode 100644
index 000000000..92f9b73b8
--- /dev/null
+++ b/tests/actions/addon/custom-override/plugins/format/main.lua
@@ -0,0 +1,3 @@
+function main()
+ print("hello from custom-override")
+end
diff --git a/tests/actions/addon/custom-override/plugins/format/xmake.lua b/tests/actions/addon/custom-override/plugins/format/xmake.lua
new file mode 100644
index 000000000..446e24db1
--- /dev/null
+++ b/tests/actions/addon/custom-override/plugins/format/xmake.lua
@@ -0,0 +1,6 @@
+-- @note `format` is a builtin plugin of xmake, we use it to test that an addon
+-- is able to take over a builtin plugin
+task("format")
+ set_category("plugin")
+ set_menu {usage = "xmake format", description = "Say hello instead of formatting.", options = {}}
+ on_run("main")
diff --git a/tests/actions/addon/custom-package/addon.lua b/tests/actions/addon/custom-package/addon.lua
new file mode 100644
index 000000000..56d8300f3
--- /dev/null
+++ b/tests/actions/addon/custom-package/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-package")
+ set_description("the addon which provides a custom package")
diff --git a/tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h b/tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h
new file mode 100644
index 000000000..ca969acd2
--- /dev/null
+++ b/tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h
@@ -0,0 +1,6 @@
+#pragma once
+#include <stdio.h>
+
+static inline void hello_utils_greeting(void) {
+ printf("hello from the custom package!\n");
+}
diff --git a/tests/actions/addon/custom-package/includes/packages/xmake.lua b/tests/actions/addon/custom-package/includes/packages/xmake.lua
new file mode 100644
index 000000000..5a02187fd
--- /dev/null
+++ b/tests/actions/addon/custom-package/includes/packages/xmake.lua
@@ -0,0 +1,13 @@
+-- an addon can ship the package definitions, so that a project only needs
+-- includes("@addon/custom-package/packages") and add_requires("hello-utils")
+package("hello-utils")
+ set_kind("library", {headeronly = true})
+ set_description("the custom package of the tests")
+
+ -- the sources are shipped by this addon, so it can be installed offline
+ set_sourcedir(path.join(os.scriptdir(), "src"))
+
+ on_install(function (package)
+ os.cp("include", package:installdir())
+ end)
+package_end()
diff --git a/tests/actions/addon/custom-plugin/addon.lua b/tests/actions/addon/custom-plugin/addon.lua
new file mode 100644
index 000000000..ba4515c78
--- /dev/null
+++ b/tests/actions/addon/custom-plugin/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-plugin")
+ set_description("the addon which provides a custom plugin")
diff --git a/tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua b/tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua
new file mode 100644
index 000000000..0cf3a0386
--- /dev/null
+++ b/tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua
@@ -0,0 +1,5 @@
+import("core.base.option")
+
+function main()
+ print("hello from custom-plugin: %s", option.get("name") or "world")
+end
diff --git a/tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua b/tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua
new file mode 100644
index 000000000..7976a1d0e
--- /dev/null
+++ b/tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua
@@ -0,0 +1,5 @@
+task("hello_addon")
+ set_category("plugin")
+ set_menu {usage = "xmake hello_addon", description = "Say hello from the addon.",
+ options = {{'n', "name", "kv", nil, "Set the name."}}}
+ on_run("main")
diff --git a/tests/actions/addon/custom-rule/addon.lua b/tests/actions/addon/custom-rule/addon.lua
new file mode 100644
index 000000000..2e9b68ab5
--- /dev/null
+++ b/tests/actions/addon/custom-rule/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-rule")
+ set_description("the addon which provides a custom rule")
diff --git a/tests/actions/addon/custom-rule/modules/greeting.lua b/tests/actions/addon/custom-rule/modules/greeting.lua
new file mode 100644
index 000000000..f247d21ca
--- /dev/null
+++ b/tests/actions/addon/custom-rule/modules/greeting.lua
@@ -0,0 +1,3 @@
+function main(name)
+ return "hello from custom-rule: " .. name
+end
diff --git a/tests/actions/addon/custom-rule/rules/hello/xmake.lua b/tests/actions/addon/custom-rule/rules/hello/xmake.lua
new file mode 100644
index 000000000..07638387d
--- /dev/null
+++ b/tests/actions/addon/custom-rule/rules/hello/xmake.lua
@@ -0,0 +1,6 @@
+rule("hello")
+ on_load(function (target)
+ -- the addon code refers to its own resources with `@self`
+ import("@self.greeting")
+ print("custom-rule: %s", greeting(target:name()))
+ end)
diff --git a/tests/actions/addon/custom-template/addon.lua b/tests/actions/addon/custom-template/addon.lua
new file mode 100644
index 000000000..f01eda9c9
--- /dev/null
+++ b/tests/actions/addon/custom-template/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-template")
+ set_description("the addon which provides a custom project template")
diff --git a/tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c b/tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c
new file mode 100644
index 000000000..115a2a792
--- /dev/null
+++ b/tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+ printf("hello from the custom-template addon!\n");
+ return 0;
+}
diff --git a/tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua b/tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua
new file mode 100644
index 000000000..33c36ec7e
--- /dev/null
+++ b/tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua
@@ -0,0 +1,5 @@
+add_rules("mode.debug", "mode.release")
+
+target("${TARGET_NAME}")
+ set_kind("binary")
+ add_files("src/*.c")
diff --git a/tests/actions/addon/custom-toolchain/addon.lua b/tests/actions/addon/custom-toolchain/addon.lua
new file mode 100644
index 000000000..8537b3272
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/addon.lua
@@ -0,0 +1,6 @@
+addon("custom-toolchain")
+ set_description("the addon which provides a custom toolchain")
+ set_sourcedir("src")
+ -- the tool modules are imported with their plain names by the internal calls,
+ -- e.g. find_tool("mycl6x"), import("core.tools.mycl6x")
+ add_globalmodules("core.tools.mycl6x", "detect.tools.find_mycl6x")
diff --git a/tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua b/tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua
new file mode 100644
index 000000000..972242c98
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua
@@ -0,0 +1,12 @@
+-- the tool module of the my-c6000 toolchain
+--
+-- @note our compiler behaves like gcc, it's just a wrapper of the host one, so we only
+-- inherit it here, a real one implements the tool interfaces itself, e.g. `init`,
+-- `nf_define`, `compargv`, `link`, @see tests/apis/custom_toolchain
+--
+inherit("core.tools.gcc")
+
+-- init it
+function init(self)
+ self:set("cxflags", "-DMY_C6000_TOOL")
+end
diff --git a/tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua b/tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua
new file mode 100644
index 000000000..c0ed37914
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua
@@ -0,0 +1,15 @@
+import("lib.detect.find_program")
+
+-- find the compiler of the my-c6000 toolchain
+--
+-- @note it's just the host compiler, we only need a real program to build with,
+-- the point of this test is that this finder itself comes from the addon
+--
+function main(opt)
+ for _, name in ipairs({"gcc", "clang", "cc"}) do
+ local program = find_program(name, opt)
+ if program then
+ return program
+ end
+ end
+end
diff --git a/tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua b/tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua
new file mode 100644
index 000000000..9b023bac7
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua
@@ -0,0 +1,19 @@
+-- a custom toolchain distributed as an addon
+--
+-- @see tests/apis/custom_toolchain for the same toolchain maintained inside a project,
+-- distributing it as an addon is the recommended way
+--
+toolchain("my-c6000")
+ set_kind("standalone")
+ set_description("the custom toolchain of the tests")
+
+ set_toolset("cc", "mycl6x")
+ set_toolset("ld", "mycl6x")
+
+ on_check(function (toolchain)
+ return import("lib.detect.find_tool")("mycl6x")
+ end)
+
+ on_load(function (toolchain)
+ toolchain:add("cxflags", "-DMY_C6000")
+ end)
diff --git a/tests/actions/addon/projects/autofetch-badname/xmake.lua b/tests/actions/addon/projects/autofetch-badname/xmake.lua
new file mode 100644
index 000000000..ffbf6cbe4
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-badname/xmake.lua
@@ -0,0 +1,5 @@
+-- the `addon` name is reserved for the addon references
+add_addons("addon")
+
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/autofetch-build/src/main.c b/tests/actions/addon/projects/autofetch-build/src/main.c
new file mode 100644
index 000000000..43509bfeb
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-build/src/main.c
@@ -0,0 +1,14 @@
+// the addons must provide the option and the toolchain of this project
+#ifndef MYOPTION
+# error the option of the custom-include addon is not found!
+#endif
+#ifndef MY_C6000
+# error the toolchain of the custom-toolchain addon is not used!
+#endif
+#ifndef MY_C6000_TOOL
+# error the tool module of the custom-toolchain addon is not used!
+#endif
+
+int main(int argc, char** argv) {
+ return 0;
+}
diff --git a/tests/actions/addon/projects/autofetch-build/xmake.lua b/tests/actions/addon/projects/autofetch-build/xmake.lua
new file mode 100644
index 000000000..862ddf676
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-build/xmake.lua
@@ -0,0 +1,14 @@
+-- the addons which this project needs, they are installed automatically
+add_addons("custom-include", "custom-rule", "custom-toolchain")
+
+-- the option comes from the includes file of an addon
+includes("@addon/custom-include/check")
+
+target("hello")
+ set_kind("binary")
+ add_files("src/main.c")
+ add_rules("@addon/custom-rule/hello")
+ set_toolchains("@addon/custom-toolchain/my-c6000")
+ if has_config("myoption") then
+ add_defines("MYOPTION")
+ end
diff --git a/tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock b/tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock
new file mode 100644
index 000000000..750a8e609
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock
@@ -0,0 +1,9 @@
+{
+ __meta__ = {
+ version = "1.0"
+ },
+ -- this version is not installed, we only install `latest` from the local directory
+ ["custom-include"] = {
+ version = "v9.9.9"
+ }
+}
diff --git a/tests/actions/addon/projects/autofetch-lockmiss/xmake.lua b/tests/actions/addon/projects/autofetch-lockmiss/xmake.lua
new file mode 100644
index 000000000..a96a4e713
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-lockmiss/xmake.lua
@@ -0,0 +1,4 @@
+add_addons("custom-include")
+
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/autofetch/xmake.lua b/tests/actions/addon/projects/autofetch/xmake.lua
new file mode 100644
index 000000000..75c909904
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch/xmake.lua
@@ -0,0 +1,8 @@
+-- the addons which this project needs, they are installed automatically
+add_addons("custom-include")
+
+-- the addon is installed automatically, so we can use its includes file here
+includes("@addon/custom-include/check")
+
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/custom-package/src/main.c b/tests/actions/addon/projects/custom-package/src/main.c
new file mode 100644
index 000000000..12208c880
--- /dev/null
+++ b/tests/actions/addon/projects/custom-package/src/main.c
@@ -0,0 +1,6 @@
+#include <hello_utils.h>
+
+int main(int argc, char** argv) {
+ hello_utils_greeting();
+ return 0;
+}
diff --git a/tests/actions/addon/projects/custom-package/xmake.lua b/tests/actions/addon/projects/custom-package/xmake.lua
new file mode 100644
index 000000000..1c967020d
--- /dev/null
+++ b/tests/actions/addon/projects/custom-package/xmake.lua
@@ -0,0 +1,9 @@
+-- the package definitions come from the addon
+includes("@addon/custom-package/packages")
+
+add_requires("hello-utils")
+
+target("test")
+ set_kind("binary")
+ add_files("src/main.c")
+ add_packages("hello-utils")
diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua
new file mode 100644
index 000000000..fcc5c2efc
--- /dev/null
+++ b/tests/actions/addon/test.lua
@@ -0,0 +1,507 @@
+import("core.base.global")
+import("lib.detect.find_program")
+
+-- the addon fixtures, each of them provides one kind of payload only
+--
+-- @note they are independent from each other, @see tests/actions/addon/custom-*
+
+function _addondir(name)
+ return path.join(os.scriptdir(), name)
+end
+
+function _remove(name)
+ try { function () os.runv("xmake", {"addon", "--remove", "--force", name}) end }
+end
+
+-- install the given addon fixture from its local directory
+function _install(name)
+ _remove(name)
+ os.runv("xmake", {"addon", "--install", _addondir(name)})
+end
+
+-- run the given function with the addons installed, we always remove them again
+function _with_addons(names, func)
+ for _, name in ipairs(names) do
+ _install(name)
+ end
+ try
+ {
+ func,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ for _, name in ipairs(names) do
+ _remove(name)
+ end
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+end
+
+-- create a temporary repository which indexes the given addon fixtures, and register it
+--
+-- @note the recipes only point at the fixtures with `set_sourcedir`, we need not generate any payload
+function _with_repo(recipes, func)
+
+ -- @note installing from a repository goes through the package manager, and it always
+ -- needs git, which cannot be installed on the hosts without a package manager,
+ -- e.g. dragonflybsd
+ if not find_program("git") then
+ print("git not found, we skip the repository tests!")
+ return
+ end
+
+ local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
+ local reponame = "addon-test-repo-" .. suffix
+ local repodir = os.tmpfile() .. ".addon-repo"
+ for name, body in pairs(recipes) do
+ io.writefile(path.join(repodir, "addons", name:sub(1, 1), name, "xmake.lua"),
+ ("package(%q)\n set_kind(\"addon\")\n set_description(\"the addon fixture of the tests\")\n %s\n"):format(name, body))
+ end
+
+ -- register the repository and clear the quick search cache, it will be rebuilt on the next search
+ local cachefile = path.join(global.cachedir(), "repository")
+ local cache = os.isfile(cachefile) and io.load(cachefile) or {}
+ cache.repositories = cache.repositories or {}
+
+ -- a killed test run may leave its temporary repository registered, and a dangling
+ -- repository breaks every following xrepo command, so we drop them here
+ for name, dirs in pairs(cache.repositories) do
+ if name:startswith("addon-test-repo-") and not os.isdir(dirs[1]) then
+ cache.repositories[name] = nil
+ end
+ end
+ cache.repositories[reponame] = {repodir}
+ io.save(cachefile, cache)
+ os.tryrm(path.join(global.cachedir(), "quick_search"))
+
+ try
+ {
+ function ()
+ func(reponame)
+ end,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ for name, _ in pairs(recipes) do
+ _remove(name)
+ end
+ local cache = os.isfile(cachefile) and io.load(cachefile) or {}
+ if cache.repositories then
+ cache.repositories[reponame] = nil
+ end
+ io.save(cachefile, cache)
+ os.tryrm(path.join(global.cachedir(), "quick_search"))
+ os.tryrm(repodir)
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+end
+
+-- run the given command in a temporary project and return its output
+function _run_project(content, argv, opt)
+ opt = opt or {}
+ local projectdir = os.tmpfile() .. ".addon-project"
+ os.tryrm(projectdir)
+ io.writefile(path.join(projectdir, "xmake.lua"), content)
+ for file, filecontent in pairs(opt.files or {}) do
+ io.writefile(path.join(projectdir, file), filecontent)
+ end
+ local oldir = os.cd(projectdir)
+ local out, errors
+ try
+ {
+ function () out = os.iorunv("xmake", argv) end,
+ catch { function (e) errors = e end },
+ finally
+ {
+ function ()
+ os.cd(oldir)
+ os.tryrm(projectdir)
+ end
+ }
+ }
+ if errors then
+ raise(errors)
+ end
+ return out
+end
+
+function _config_project(content)
+ return _run_project(content, {"config", "-y"})
+end
+
+-- copy the given fixture project to a temporary directory and run the given function in it
+--
+-- @note we cannot run them in place, they would generate the lock and the build files,
+-- @see tests/actions/addon/projects
+--
+function _with_project(name, func)
+ local projectdir = os.tmpfile() .. ".addon-project"
+ os.tryrm(projectdir)
+ os.mkdir(projectdir)
+ os.cp(path.join(os.scriptdir(), "projects", name, "*"), projectdir)
+ local oldir = os.cd(projectdir)
+ try
+ {
+ function ()
+ func(projectdir)
+ end,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ os.cd(oldir)
+ os.tryrm(projectdir)
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+end
+
+-- only the payloads should be installed, our own files should not
+function test_install(t)
+ _with_addons({"custom-toolchain"}, function ()
+
+ -- it has the `src` layout, e.g. set_sourcedir("src")
+ local installdir = path.join(global.directory(), "addons", "custom-toolchain", "latest")
+ t:require(os.isdir(path.join(installdir, "toolchains")))
+ t:require(os.isdir(path.join(installdir, "modules")))
+ for _, ourfile in ipairs({"src", "addon.lua"}) do
+ t:require_not(os.exists(path.join(installdir, ourfile)))
+ end
+
+ -- the addon describes itself, we should get its description from the manifest
+ t:require(os.iorunv("xmake", {"addon", "--list"}):find("the addon which provides a custom toolchain", 1, true))
+ end)
+end
+
+-- an addon can provide a plugin, e.g. xmake hello_addon
+function test_plugin(t)
+ _with_addons({"custom-plugin"}, function ()
+ t:require(os.iorunv("xmake", {"hello_addon", "-n", "xmake"}):find("hello from custom-plugin: xmake", 1, true))
+ end)
+
+ -- it should not be runnable after removing it
+ t:require_not(try { function () os.runv("xmake", {"hello_addon"}); return true end })
+end
+
+-- an addon can provide a rule, e.g. add_rules("@addon/custom-rule/hello")
+function test_rule(t)
+ local projectfile = [[
+target("test")
+ set_kind("phony")
+ add_rules("@addon/custom-rule/hello")
+]]
+ _with_addons({"custom-rule"}, function ()
+ -- the rule imports a module of its own addon with `@self`
+ t:require(_config_project(projectfile):find("custom-rule: hello from custom-rule: test", 1, true))
+ end)
+
+ -- it should fail if the addon which provides it is not installed
+ t:require_not(try { function () _config_project(projectfile); return true end })
+end
+
+-- an addon can provide a module, e.g. import("@addon.custom-module.greeting")
+function test_module(t)
+ _with_addons({"custom-module"}, function ()
+ local script = "import(\"@addon.custom-module.greeting\"); print(greeting(\"xmake\"))"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from custom-module: xmake", 1, true))
+
+ -- the addon modules are namespaced, they cannot be imported with their plain names
+ t:require_not(try { function () os.runv("xmake", {"lua", "-c", "import(\"greeting\")"}); return true end })
+ end)
+end
+
+-- an addon can provide an includes file, e.g. includes("@addon/custom-include/check")
+function test_include(t)
+ _with_addons({"custom-include"}, function ()
+ local out = _config_project([[
+includes("@addon/custom-include/check")
+target("test")
+ set_kind("phony")
+]])
+ t:require(out:find("custom-include: includes check is loaded", 1, true))
+ end)
+end
+
+-- an addon can provide a project template, e.g. xmake create -t customaddon.hello
+function test_template(t)
+ _with_addons({"custom-template"}, function ()
+ t:require(os.iorunv("xmake", {"create", "--list", "-l", "c"}):find("customaddon.hello", 1, true))
+
+ local projectdir = os.tmpfile() .. ".addon-project"
+ os.tryrm(projectdir)
+ os.runv("xmake", {"create", "-l", "c", "-t", "customaddon.hello", "-P", projectdir})
+ t:require(os.isfile(path.join(projectdir, "src", "main.c")))
+ os.tryrm(projectdir)
+ end)
+end
+
+-- an addon can ship the package definitions in its includes file, so that a project
+-- only needs `add_requires`, @see tests/actions/addon/custom-package
+function test_include_packages(t)
+
+ -- @note we need a compiler here, the project links against the package
+ if not (find_program("gcc") or find_program("clang") or find_program("cc")) then
+ return
+ end
+ _with_addons({"custom-package"}, function ()
+ _with_project("custom-package", function ()
+ t:require(os.iorunv("xmake", {"build", "-y"}):find("build ok", 1, true))
+ t:require(os.iorunv("xmake", {"run"}):find("hello from the custom package", 1, true))
+ end)
+ end)
+end
+
+-- an addon can provide a toolchain and its tool modules
+--
+-- @see tests/apis/custom_toolchain for the same toolchain maintained inside a project
+function test_toolchain(t)
+ _with_addons({"custom-toolchain"}, function ()
+
+ -- the toolchain can be loaded with the addon name
+ local script = "import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/custom-toolchain/my-c6000\"):get(\"description\"))"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("the custom toolchain of the tests", 1, true))
+
+ -- its tool modules are exported by the manifest, so the internal calls can import them
+ -- with their plain names, e.g. add_globalmodules("core.tools.mycl6x")
+ --
+ -- @note there is no builtin `mycl6x`, so they can only come from this addon
+ local script2 = "import(\"core.tools.mycl6x\"); assert(mycl6x.compargv, \"invalid tool module!\"); " ..
+ "import(\"lib.detect.find_tool\"); assert(find_tool(\"mycl6x\"), \"mycl6x not found!\")"
+ os.runv("xmake", {"lua", "-c", script2})
+
+ -- and a project can build with it, the flags of the toolchain and of its tool module
+ -- are both checked by the source file
+ --
+ -- @note its compiler is just the host one, so we can only build with it if there is one
+ if find_program("gcc") or find_program("clang") or find_program("cc") then
+ _run_project([[
+target("test")
+ set_kind("binary")
+ set_toolchains("@addon/custom-toolchain/my-c6000")
+ add_files("src/main.c")
+]], {"build", "-y"}, {files = {["src/main.c"] = [[
+#ifndef MY_C6000
+# error the flags of the addon toolchain are not used!
+#endif
+#ifndef MY_C6000_TOOL
+# error the flags of its tool module are not used!
+#endif
+int main(int argc, char** argv) { return 0; }
+]]}})
+ end
+ end)
+end
+
+-- the addons which a project declares with `add_addons` are installed automatically
+--
+-- @note they must be installed before loading the project, it may use their includes files
+function test_autofetch(t)
+ local recipes = {["custom-include"] = ("set_sourcedir(%q)"):format(_addondir("custom-include"))}
+ _with_repo(recipes, function ()
+ _remove("custom-include")
+ _with_project("autofetch", function (projectdir)
+
+ -- it should be installed when loading the project, so that its includes file can be found,
+ -- and we should tell the user why we install something, it may need to confirm and download
+ local output = os.iorunv("xmake", {"config", "-y"})
+ t:require(output:find("custom-include: includes check is loaded", 1, true))
+ t:require(output:find("this project needs the addons", 1, true))
+
+ -- and it should be locked
+ local lockfile = path.join(projectdir, "xmake-addons.lock")
+ t:require(os.isfile(lockfile))
+ t:require(io.load(lockfile)["custom-include"] ~= nil)
+
+ -- we should not install it again
+ t:require_not(os.iorunv("xmake", {"config", "-y"}):find("install custom-include", 1, true))
+ end)
+ end)
+end
+
+-- every command builds the option menu, which merges the project tasks in a best-effort way,
+-- so the commands which need not the project should never install its addons
+function test_autofetch_skipped_for_option_menu(t)
+ local recipes = {["custom-include"] = ("set_sourcedir(%q)"):format(_addondir("custom-include"))}
+ _with_repo(recipes, function ()
+ _remove("custom-include")
+ _with_project("autofetch", function (projectdir)
+ os.runv("xmake", {"addon", "--list"})
+ os.runv("xmake", {"lua", "-c", "print(\"hello\")"})
+ t:require_not(os.isfile(path.join(projectdir, "xmake-addons.lock")))
+ end)
+ end)
+end
+
+-- a complete project which declares its addons with `add_addons` and builds with them,
+-- @see tests/actions/addon/projects/autofetch-build
+function test_autofetch_build(t)
+
+ -- @note the compiler of the custom toolchain is just the host one,
+ -- so we can only build it if there is one
+ if not (find_program("gcc") or find_program("clang") or find_program("cc")) then
+ return
+ end
+
+ local names = {"custom-include", "custom-rule", "custom-toolchain"}
+ local recipes = {}
+ for _, name in ipairs(names) do
+ recipes[name] = ("set_sourcedir(%q)"):format(_addondir(name))
+ end
+ _with_repo(recipes, function ()
+ for _, name in ipairs(names) do
+ _remove(name)
+ end
+ _with_project("autofetch-build", function (projectdir)
+
+ -- all of them should be installed when loading the project, and it should build
+ -- with their includes file, rule and toolchain, @see src/main.c
+ local output = os.iorunv("xmake", {"build", "-y"})
+ t:require(output:find("custom-include: includes check is loaded", 1, true))
+ t:require(output:find("custom-rule: hello from custom-rule: hello", 1, true))
+ t:require(output:find("build ok", 1, true))
+
+ -- and all of them should be locked
+ local lockinfo = io.load(path.join(projectdir, "xmake-addons.lock"))
+ for _, name in ipairs(names) do
+ t:require(lockinfo[name] ~= nil)
+ end
+ end)
+ end)
+end
+
+-- a locked version which is not installed must not hide the addon
+--
+-- @note the auto-fetch installs the locked version, but the commands which do not load
+-- the project content only pin it, e.g. `xmake lua`, @see project._pin_addons()
+--
+function test_autofetch_lock_missing_version(t)
+ _with_addons({"custom-include"}, function ()
+ _with_project("autofetch-lockmiss", function ()
+ local script = "import(\"core.package.addon\"); print(addon.addons()[\"custom-include\"] ~= nil)"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("true", 1, true))
+ end)
+ end)
+end
+
+-- the declared addons are checked, e.g. the reserved names
+function test_autofetch_invalid(t)
+ _with_project("autofetch-badname", function ()
+ t:require_not(try { function () os.runv("xmake", {"config", "-y"}); return true end })
+ end)
+end
+
+-- the addons can be installed from a repository, by plain name and by repo@name
+function test_install_from_repo(t)
+ local recipes = {["custom-plugin"] = ("set_sourcedir(%q)"):format(_addondir("custom-plugin")),
+ ["custom-module"] = ("set_sourcedir(%q)"):format(_addondir("custom-module"))}
+ _with_repo(recipes, function (reponame)
+ os.runv("xmake", {"addon", "--install", "-y", "custom-plugin"})
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+
+ os.runv("xmake", {"addon", "--remove", "custom-plugin"})
+ os.runv("xmake", {"addon", "--install", "-y", reponame .. "@custom-plugin"})
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+
+ -- it should be searchable, and the addons should not be found by the package search
+ --
+ -- @note we cannot run the `xrepo` program here, it may not be in the PATH, e.g. on the ci,
+ -- and `xrepo search` is just a wrapper of it
+ --
+ t:require(os.iorunv("xmake", {"addon", "--search", "custom-plugin"}):find("custom-plugin", 1, true))
+ t:require_not(os.iorunv("xmake", {"lua", "private.xrepo", "search", "custom-plugin"}):find("custom-plugin", 1, true))
+
+ -- and several of them can be installed in one shot, they are resolved and installed together
+ os.runv("xmake", {"addon", "--remove", "--force", "custom-plugin"})
+ os.runv("xmake", {"addon", "--install", "-y", "custom-plugin", reponame .. "@custom-module"})
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+ t:require(os.iorunv("xmake", {"lua", "-c", "import(\"@addon.custom-module.greeting\"); print(greeting(\"xmake\"))"})
+ :find("hello from custom-module: xmake", 1, true))
+ end)
+end
+
+-- an addon can depend on the other addons, they are installed and activated together
+function test_addon_deps(t)
+ local recipes = {
+ ["custom-module"] = ("set_sourcedir(%q)"):format(_addondir("custom-module")),
+ ["custom-plugin"] = ("set_sourcedir(%q)\n add_deps(\"custom-module\", {kind = \"addon\"})"):format(_addondir("custom-plugin"))
+ }
+ _with_repo(recipes, function ()
+ os.runv("xmake", {"addon", "--install", "-y", "custom-plugin"})
+ local out = os.iorunv("xmake", {"addon", "--list"})
+ t:require(out:find("custom-plugin", 1, true))
+ t:require(out:find("custom-module", 1, true))
+
+ -- we cannot remove the dependency, it's depended on by the other addon
+ t:require_not(try { function () os.runv("xmake", {"addon", "--remove", "custom-module"}); return true end })
+ end)
+end
+
+-- the plugins and the templates are not namespaced, the conflicts should be rejected when installing
+function test_install_conflicts(t)
+ _with_addons({"custom-plugin"}, function ()
+
+ -- this addon provides the same plugin name
+ local clonedir = os.tmpfile() .. ".addon-clone"
+ os.tryrm(clonedir)
+ io.writefile(path.join(clonedir, "addon.lua"), "addon(\"custom-plugin-clone\")\n")
+ io.writefile(path.join(clonedir, "plugins", "hello_addon", "xmake.lua"),
+ "task(\"hello_addon\")\n set_category(\"plugin\")\n set_menu {}\n on_run(function () end)\n")
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", clonedir}); return true end })
+ os.tryrm(clonedir)
+
+ -- and the other commands should still work
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+ end)
+end
+
+-- an addon is able to take over a builtin plugin, so we can move a deprecated builtin
+-- plugin to an addon, e.g. `xmake format`
+function test_install_override(t)
+
+ -- the builtin plugin is used if we do not install the addon
+ t:require_not(os.iorunv("xmake", {"format", "--help"}):find("Say hello instead of formatting", 1, true))
+
+ _with_addons({"custom-override"}, function ()
+ local out = os.iorunv("xmake", {"format"})
+ t:require(out:find("hello from custom-override", 1, true))
+
+ -- and it should not be reported as a conflict
+ t:require_not(out:find("conflicts", 1, true))
+ t:require(os.iorunv("xmake", {"format", "--help"}):find("Say hello instead of formatting", 1, true))
+ end)
+
+ -- the builtin plugin is used again after removing the addon
+ t:require_not(os.iorunv("xmake", {"format", "--help"}):find("Say hello instead of formatting", 1, true))
+end
+
+-- the invalid installs should fail, and the `addon` name is reserved for the addon references
+function test_invalid(t)
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "addon-test-missing"}); return true end })
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "somerepo@.."}); return true end })
+
+ -- the addon name in its manifest must match the package name which distributes it
+ local recipes = {["custom-plugin-badname"] = ("set_sourcedir(%q)"):format(_addondir("custom-plugin"))}
+ _with_repo(recipes, function ()
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "custom-plugin-badname"}); return true end })
+ end)
+
+ -- the `addon` name is reserved
+ t:require_not(try { function () _config_project([[
+add_requires("addon")
+target("test")
+ set_kind("phony")
+]]); return true end })
+end
diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua
index ce68bcda8..fe4a182f2 100644
--- a/tests/modules/binutils/test.lua
+++ b/tests/modules/binutils/test.lua
@@ -81,6 +81,130 @@ function test_format(t)
os.tryrm(tempdir)
end
+function test_bin2elf(t)
+ local tempdir = "temp/binutils_bin2elf"
+ os.tryrm(tempdir)
+ os.mkdir(tempdir)
+
+ -- input binary payload
+ local input = path.join(tempdir, "data.bin")
+ io.writefile(input, "hello world payload", {encoding = "binary"})
+
+ -- parse the ELF header fields we care about (class, data encoding, machine, flags)
+ local function _parse_elf_header(objectfile)
+ local data = io.readfile(objectfile, {encoding = "binary"})
+ t:require(data ~= nil and #data >= 24)
+ -- magic
+ t:are_equal(string.byte(data, 1), 0x7f)
+ t:are_equal(string.byte(data, 2), string.byte("E"))
+ t:are_equal(string.byte(data, 3), string.byte("L"))
+ t:are_equal(string.byte(data, 4), string.byte("F"))
+ local class = string.byte(data, 5) -- e_ident[EI_CLASS]: 1 = 32-bit, 2 = 64-bit
+ local encode = string.byte(data, 6) -- e_ident[EI_DATA]: 1 = LSB, 2 = MSB
+ local bigendian = (encode == 2)
+ -- read a target-endian integer of nbytes at 1-based index
+ local function _readint(index, nbytes)
+ local value = 0
+ for i = 0, nbytes - 1 do
+ local byte = string.byte(data, index + i)
+ if bigendian then
+ value = value * 256 + byte
+ else
+ value = value + byte * (256 ^ i)
+ end
+ end
+ return value
+ end
+ local machine = _readint(19, 2) -- e_machine at offset 18
+ -- e_flags: offset 36 for 32-bit, 48 for 64-bit
+ local flags = (class == 2) and _readint(49, 4) or _readint(37, 4)
+ return {class = class, encode = encode, machine = machine, flags = flags}
+ end
+
+ local function _gen(arch)
+ local objectfile = path.join(tempdir, arch .. ".o")
+ binutils.bin2obj(input, objectfile, {format = "elf", arch = arch, basename = "test"})
+ return _parse_elf_header(objectfile)
+ end
+
+ -- loong64: 64-bit class, LoongArch machine (issue: was wrongly detected as 32-bit)
+ local loong64 = _gen("loong64")
+ t:are_equal(loong64.class, 2)
+ t:are_equal(loong64.encode, 1)
+ t:are_equal(loong64.machine, 0x102)
+ t:are_equal(loong64.flags, 0x43) -- double-float ABI + object ABI v1
+
+ -- riscv64: double-float ABI in e_flags (issue: was soft-float, e_flags == 0)
+ local riscv64 = _gen("riscv64")
+ t:are_equal(riscv64.class, 2)
+ t:are_equal(riscv64.machine, 0xf3)
+ t:are_equal(riscv64.flags, 0x5) -- RVC + double-float
+
+ -- riscv (32-bit): same machine/flags, 32-bit class
+ local riscv = _gen("riscv")
+ t:are_equal(riscv.class, 1)
+ t:are_equal(riscv.machine, 0xf3)
+ t:are_equal(riscv.flags, 0x5)
+
+ -- s390x: big-endian (issue: was wrongly little-endian)
+ local s390x = _gen("s390x")
+ t:are_equal(s390x.class, 2)
+ t:are_equal(s390x.encode, 2) -- MSB
+ t:are_equal(s390x.machine, 0x16)
+
+ -- x86_64: unchanged, little-endian 64-bit
+ local x86_64 = _gen("x86_64")
+ t:are_equal(x86_64.class, 2)
+ t:are_equal(x86_64.encode, 1)
+ t:are_equal(x86_64.machine, 0x3e)
+ t:are_equal(x86_64.flags, 0)
+
+ -- mips is big-endian, mipsel is little-endian
+ -- 32-bit variants use the o32 ABI + CPIC (e_flags == 0x1004)
+ local mips = _gen("mips")
+ t:are_equal(mips.encode, 2)
+ t:are_equal(mips.machine, 0x08)
+ t:are_equal(mips.flags, 0x1004)
+ local mipsel = _gen("mipsel")
+ t:are_equal(mipsel.encode, 1)
+ t:are_equal(mipsel.machine, 0x08)
+ t:are_equal(mipsel.flags, 0x1004)
+
+ -- 64-bit variants use the n64 ABI (implied by ELFCLASS64) + CPIC (e_flags == 0x4)
+ local mips64 = _gen("mips64")
+ t:are_equal(mips64.class, 2)
+ t:are_equal(mips64.encode, 2)
+ t:are_equal(mips64.machine, 0x08)
+ t:are_equal(mips64.flags, 0x4)
+ local mips64el = _gen("mips64el")
+ t:are_equal(mips64el.class, 2)
+ t:are_equal(mips64el.encode, 1)
+ t:are_equal(mips64el.machine, 0x08)
+ t:are_equal(mips64el.flags, 0x4)
+
+ -- xmake has no separate ppc64le arch (find_platform maps powerpc64le -> ppc64) and
+ -- ppc64le is the dominant modern target, so a plain "ppc64" is treated as ppc64le:
+ -- little-endian + OpenPOWER ELFv2 ABI (e_flags low bits == 2)
+ local ppc64 = _gen("ppc64")
+ t:are_equal(ppc64.encode, 1)
+ t:are_equal(ppc64.machine, 0x15)
+ t:are_equal(ppc64.flags, 0x2)
+
+ -- explicit ppc64le behaves the same
+ local ppc64le = _gen("ppc64le")
+ t:are_equal(ppc64le.encode, 1)
+ t:are_equal(ppc64le.machine, 0x15)
+ t:are_equal(ppc64le.flags, 0x2)
+
+ -- explicit big-endian ppc64be keeps the ELFv1 ABI
+ local ppc64be = _gen("ppc64be")
+ t:are_equal(ppc64be.encode, 2)
+ t:are_equal(ppc64be.machine, 0x15)
+ t:are_equal(ppc64be.flags, 0x1)
+
+ os.tryrm(tempdir)
+end
+
function test_deplibs(t)
local tempdir = "temp/binutils_deplibs"
os.tryrm(tempdir)
diff --git a/tests/modules/project_depend/test.lua b/tests/modules/project_depend/test.lua
new file mode 100644
index 000000000..2df79ba16
--- /dev/null
+++ b/tests/modules/project_depend/test.lua
@@ -0,0 +1,19 @@
+import("core.project.depend")
+
+function test_nested_values_append(t)
+ local dependinfo = {values = {{"clang", "-m64"}}}
+ local changed = depend.is_changed(dependinfo, {values = {{"clang", "-m64", "-DFEATURE_ON"}}})
+ t:require(changed)
+end
+
+function test_nested_values_remove(t)
+ local dependinfo = {values = {{"clang", "-m64", "-DFEATURE_ON"}}}
+ local changed = depend.is_changed(dependinfo, {values = {{"clang", "-m64"}}})
+ t:require(changed)
+end
+
+function test_nested_values_unchanged(t)
+ local dependinfo = {values = {{"clang", "-m64", "-DFEATURE_ON"}}}
+ local changed = depend.is_changed(dependinfo, {values = {{"clang", "-m64", "-DFEATURE_ON"}}})
+ t:require_not(changed)
+end
diff --git a/tests/modules/semver/test.lua b/tests/modules/semver/test.lua
index 1ab6dc8aa..57d213f4a 100755
--- a/tests/modules/semver/test.lua
+++ b/tests/modules/semver/test.lua
@@ -7,6 +7,12 @@ function _check_semver_select(t, results, required_ver, versions, tags, branches
t:are_equal(source, results[2])
end
+function _check_semver_select_failed(t, required_ver, versions, tags, branches)
+ t:will_raise(function()
+ semver.select(required_ver, versions or {}, tags or {}, branches or {})
+ end, "unable to select version")
+end
+
-- test select version
function test_semver_select(t)
@@ -18,15 +24,101 @@ function test_semver_select(t)
, "^1.5.0"
,{"1.4.0", "1.5.0", "1.5.1"})
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "3.53.0+200"
+ , {"3.53.0+0", "3.53.0+100", "3.53.0+200"})
+
+ _check_semver_select_failed(t, "3.53.0+999", {"3.53.0+100"})
+
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "3.53.0"
+ , {"3.53.0+0", "3.53.0+100", "3.53.0+200"})
+
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "3.53.0"
+ , {"3.53.0+200", "3.53.0+100", "3.53.0+0"})
+
+ _check_semver_select(t, {"3.53.0+beta", "version"}
+ , "3.53.0"
+ , {"3.53.0+alpha", "3.53.0+beta"})
+
+ _check_semver_select(t, {"3.53.0+beta", "version"}
+ , "3.53.0"
+ , {"3.53.0+beta", "3.53.0+alpha"})
+
+ _check_semver_select(t, {"1.2.3", "version"}
+ , "1.2.3"
+ , {"1.2.3+1", "1.2.3"})
+
+ _check_semver_select(t, {"1.2.3", "version"}
+ , "=1.2.3"
+ , {"1.2.3+7", "1.2.3"})
+
+ _check_semver_select(t, {"1.2.9", "version"}
+ , "1.2"
+ , {"1.2", "1.2.9"})
+
+ _check_semver_select(t, {"1.9.0", "version"}
+ , "^1.2.3"
+ , {"^1.2.3", "1.2.3", "1.9.0"})
+
+ _check_semver_select(t, {"1.2.3", "tag"}
+ , "1.2.3"
+ , {"v1.2.3+7"}
+ , {"1.2.3"})
+
+ _check_semver_select(t, {"v1.2.3+7", "version"}
+ , "1.2.3+7"
+ , {"v1.2.3+8", "v1.2.3+7"})
+
+ _check_semver_select(t, {"1.2.3+7", "version"}
+ , "v1.2.3+7"
+ , {"1.2.3+8", "1.2.3+7"})
+
+ _check_semver_select(t, {"3.53.0+200", "tag"}
+ , "3.53.0+200"
+ , nil
+ , {"3.53.0+0", "3.53.0+200"})
+
+ _check_semver_select_failed(t, "3.53.0+999", nil, {"3.53.0+100"})
+
+ _check_semver_select(t, {"v1.2.3+7", "tag"}
+ , "=1.2.3+7"
+ , nil
+ , {"v1.2.3+7", "v1.2.3+8"})
+
+ _check_semver_select(t, {"v1.2.3+7", "tag"}
+ , "1.2.3+7"
+ , {"1.2.3+8"}
+ , {"v1.2.3+7"})
+
_check_semver_select(t, {"master", "branch"}
, "master"
, {"1.4.0", "1.5.0", "1.5.1"}
, {"v1.2.0", "v1.6.0"}
, {"master", "dev"})
+ _check_semver_select(t, {"next", "branch"}
+ , "next"
+ , nil
+ , {"vnext"}
+ , {"next"})
+
_check_semver_select(t, {"1.5.1", "version"}
, "latest"
, {"1.4.0", "1.5.0", "1.5.1"})
+
+ _check_semver_select(t, {"1.0.0+10", "version"}
+ , "latest"
+ , {"1.0.0+9", "1.0.0+10"})
+
+ _check_semver_select(t, {"1.0.0+rev.10", "version"}
+ , "latest"
+ , {"1.0.0+rev.9", "1.0.0+rev.10"})
+
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "latest"
+ , {"3.53.0+0", "3.53.0+200", "3.53.0+100"})
end
-- select version
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp b/tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp
new file mode 100644
index 000000000..e3e6224f2
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp
@@ -0,0 +1,5 @@
+export module base;
+
+export int base_value() {
+ return 40;
+}
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp b/tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp
new file mode 100644
index 000000000..9de491171
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp
@@ -0,0 +1,7 @@
+export module hello;
+
+import base;
+
+export int answer() {
+ return base_value() + 2;
+}
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp b/tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp
new file mode 100644
index 000000000..75adbd883
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp
@@ -0,0 +1,5 @@
+import hello;
+
+int main() {
+ return answer() == 42 ? 0 : 1;
+}
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/test.lua b/tests/projects/c++/modules/precompile_reduced_bmi/test.lua
new file mode 100644
index 000000000..765290f7b
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/test.lua
@@ -0,0 +1,107 @@
+inherit(".test_base")
+import("core.base.json")
+
+local _CLANG_MIN_VER = "23"
+
+function clang_min_ver()
+ return _CLANG_MIN_VER
+end
+
+function _check_commands(config, outdata)
+ local has_precompile_reduced_bmi = outdata:find("--precompile-reduced-bmi", 1, true) ~= nil
+ local expect_precompile_reduced_bmi = config.precompile_reduced_bmi and config.two_phases
+ assert(has_precompile_reduced_bmi == expect_precompile_reduced_bmi,
+ "unexpected --precompile-reduced-bmi usage\n%s", outdata)
+
+ if config.two_phases then
+ local object_command
+ for _, line in ipairs(outdata:split("\n", {plain = true})) do
+ if line:find("hello.mpp", 1, true) and
+ (line:find("hello.mpp.o", 1, true) or line:find("hello.mpp.obj", 1, true)) and
+ line:find(" -c ", 1, true) and
+ not line:find("clang-scan-deps", 1, true) and
+ not line:find("--precompile", 1, true) then
+ object_command = line
+ break
+ end
+ end
+ assert(object_command, "module object command missing\n%s", outdata)
+ local consumes_bmi = object_command:find("hello.pcm", 1, true) ~= nil
+ assert(consumes_bmi ~= expect_precompile_reduced_bmi, "unexpected module object input\n%s", outdata)
+ end
+end
+
+function _check_incremental()
+ local outdata = os.iorun("xmake -v")
+ if outdata:find("compiling", 1, true) or
+ outdata:find("linking", 1, true) or
+ outdata:find("generating", 1, true) then
+ raise("Modules incremental compilation does not work\n%s", outdata)
+ end
+end
+
+function _check_compile_commands(config)
+ os.run("xmake project -k compile_commands")
+ local bmi_command
+ local object_command
+ for _, command in ipairs(assert(json.loadfile("compile_commands.json"))) do
+ if command.file:endswith("hello.mpp") then
+ local commandline = command.command or table.concat(command.arguments or {}, " ")
+ if commandline:find("--precompile", 1, true) then
+ bmi_command = commandline
+ elseif commandline:find("hello.mpp.o", 1, true) or commandline:find("hello.mpp.obj", 1, true) then
+ object_command = commandline
+ end
+ end
+ end
+ assert(bmi_command, "module BMI command missing from compile_commands.json")
+ assert(object_command, "module object command missing from compile_commands.json")
+ local expect_precompile_reduced_bmi = config.precompile_reduced_bmi and config.two_phases
+ assert((bmi_command:find("--precompile-reduced-bmi", 1, true) ~= nil) == expect_precompile_reduced_bmi,
+ "unexpected module BMI command\n%s", bmi_command)
+ assert(object_command:find("hello.mpp", 1, true) and not object_command:find("hello.pcm", 1, true),
+ "unexpected module object command\n%s", object_command)
+end
+
+function _configure(config)
+ local argv = {"f", "--yes", "--toolchain=" .. config.toolchain}
+ if config.platform then
+ table.join2(argv, {"-p", config.platform})
+ end
+ if config.runtimes then
+ table.insert(argv, "--runtimes=" .. config.runtimes)
+ end
+ local policies = "--policies=build.c++.modules.std:" .. (config.stdmodule and "y" or "n")
+ policies = policies .. ",build.c++.modules.fallbackscanner:" .. (config.fallbackscanner and "y" or "n")
+ policies = policies .. ",build.c++.modules.two_phases:" .. (config.two_phases and "y" or "n")
+ policies = policies .. ",build.c++.modules.clang.precompile_reduced_bmi:" .. (config.precompile_reduced_bmi and "y" or "n")
+ table.insert(argv, policies)
+ table.join2(argv, config.flags or {})
+ os.execv("xmake", argv)
+end
+
+function _check_build(config)
+ local outdata = os.iorun("xmake -rv")
+ _check_commands(config, outdata)
+
+ os.run("xmake run")
+ _check_incremental()
+ if config.two_phases then
+ _check_compile_commands(config)
+ end
+
+ if config.two_phases then
+ local toggled_config = table.clone(config)
+ toggled_config.precompile_reduced_bmi = not config.precompile_reduced_bmi
+ _configure(toggled_config)
+ local toggled_outdata = os.iorun("xmake -v")
+ _check_commands(toggled_config, toggled_outdata)
+ os.run("xmake run")
+ _check_incremental()
+ _check_compile_commands(toggled_config)
+ end
+end
+
+function main(_)
+ run_tests({compiler = "clang", version = clang_min_ver(), precompile_reduced_bmi = true, build = _check_build})
+end
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua b/tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua
new file mode 100644
index 000000000..0aa2dc516
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.release", "mode.debug")
+set_languages("c++20")
+
+target("precompile_reduced_bmi")
+ set_kind("binary")
+ add_files("src/*.cpp", "src/*.mpp")
diff --git a/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp b/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp
index 001cd7c2d..ab104076c 100644
--- a/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp
+++ b/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp
@@ -4,11 +4,9 @@ import std;
export auto my_sum2(std::size_t a, std::size_t b) -> std::size_t;
-#if defined(__GNUC__) && !defined(__clang__)
-inline
-#else
-module :private;
-#endif
- auto my_sum2(std::size_t a, std::size_t b) -> std::size_t {
+// Clang 23 implements P1857R3, which rejects module directives controlled by
+// preprocessing conditionals. This test therefore uses an inline definition.
+// See https://wg21.link/P1857R3
+inline auto my_sum2(std::size_t a, std::size_t b) -> std::size_t {
return a + a + b + b;
}
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp
new file mode 100644
index 000000000..5b9d1194e
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp
@@ -0,0 +1,5 @@
+module foo;
+
+auto my_sum(int a, int b) -> int {
+ return a + b;
+}
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp
new file mode 100644
index 000000000..4c9a649ca
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp
@@ -0,0 +1,3 @@
+export module foo;
+
+export auto my_sum(int a, int b) -> int;
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp b/tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp
new file mode 100644
index 000000000..1f87ceb59
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp
@@ -0,0 +1,8 @@
+import foo;
+
+import std;
+
+int main() {
+ std::cout << my_sum(1, 2) << std::endl;
+ return 0;
+}
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/test.lua b/tests/projects/c++/modules/stdmodules_deps_ordering/test.lua
new file mode 100644
index 000000000..e77adc4ae
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/test.lua
@@ -0,0 +1 @@
+inherit(".test_stdmodules")
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua b/tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua
new file mode 100644
index 000000000..09c3f6a94
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua
@@ -0,0 +1,19 @@
+add_rules("mode.debug", "mode.release")
+set_languages("c++latest")
+
+-- test that a plain c++ file is ordered after all the bmis it reuses from its deps,
+-- not only after the last one
+--
+-- @note foo must not import std, otherwise its bmi and the std bmi
+-- would not be built by two distinct jobs of the foo target
+--
+-- @see https://github.com/xmake-io/xmake/issues/7690
+target("foo")
+ set_kind("static")
+ add_files("src/foo.cpp")
+ add_files("src/foo.mpp", {public = true})
+
+target("main")
+ set_kind("binary")
+ add_deps("foo")
+ add_files("src/main.cpp")
diff --git a/tests/projects/c++/modules/test_base.lua b/tests/projects/c++/modules/test_base.lua
index 372229695..2d0fa3f57 100644
--- a/tests/projects/c++/modules/test_base.lua
+++ b/tests/projects/c++/modules/test_base.lua
@@ -103,6 +103,9 @@ function build_tests(toolchain_name, opt)
local policies = "--policies=build.c++.modules.std:" .. (opt.stdmodule and "y" or "n")
policies = policies .. ",build.c++.modules.fallbackscanner:" .. (opt.fallbackscanner and "y" or "n")
policies = policies .. ",build.c++.modules.two_phases:" .. (two_phases and "y" or "n")
+ if opt.precompile_reduced_bmi ~= nil then
+ policies = policies .. ",build.c++.modules.clang.precompile_reduced_bmi:" .. (opt.precompile_reduced_bmi and "y" or "n")
+ end
local platform = " "
if opt.platform then
@@ -127,7 +130,7 @@ function build_tests(toolchain_name, opt)
os.exec("xmake clean -a")
os.exec("xmake f" .. platform .. "--toolchain=" .. toolchain_name .. runtimes .. "-c --yes " .. policies .. flags)
if opt.build then
- opt.build()
+ opt.build(table.join(opt, {toolchain = toolchain_name, two_phases = two_phases}))
else
_build(opt.check_outdata)
end
@@ -150,9 +153,12 @@ function run_tests(clang_options, gcc_options, msvc_options)
if not clang_options.disable_clang_cl then
local clang_cl_options = table.clone(clang_options)
clang_cl_options.compiler = "clang-cl"
- clang_cl_options.version = clang_cl_min_ver()
+ local clang_cl_minver = clang_cl_min_ver()
+ if semver.compare(clang_cl_options.version, clang_cl_minver) < 0 then
+ clang_cl_options.version = clang_cl_minver
+ end
build_tests("clang-cl", clang_cl_options)
- build_tests("clang-cl", table.join(clang_options, {two_phases = false}))
+ build_tests("clang-cl", table.join(clang_cl_options, {two_phases = false}))
end
if clang_options.stdmodule then
wprint("std modules tests skipped for Windows clang libc++ as it's not currently supported officially")
diff --git a/tests/projects/embed/esp32/blink/boards/esp32c3/board.h b/tests/projects/embed/esp32/blink/boards/esp32c3/board.h
new file mode 100644
index 000000000..126f2c2c3
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/boards/esp32c3/board.h
@@ -0,0 +1,7 @@
+// the board configuration of esp32c3, it's selected by the `board` option
+//
+// @note gpio12-17 are the spi flash of the esp32c3, do not use them
+//
+#pragma once
+
+#define LED_GPIO 8
diff --git a/tests/projects/embed/esp32/blink/src/main.c b/tests/projects/embed/esp32/blink/src/main.c
new file mode 100644
index 000000000..b51e3b67b
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/src/main.c
@@ -0,0 +1,26 @@
+// blink the led of the board and print a heartbeat
+//
+// the led pin comes from `boards/<board>/board.h`, it's selected by the board option,
+// e.g. xmake f --board=esp32c3
+//
+// @note the entry is the esp-idf native `void app_main(void)`, and we print with
+// `esp_rom_printf`, it writes to the rom console, which is the usb port on the
+// boards booting over the native usb (usb-serial-jtag), e.g. xmake monitor
+//
+#include "board.h"
+#include "driver/gpio.h"
+#include "esp_rom_sys.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+void app_main(void)
+{
+ esp_rom_printf("blink: toggle gpio %d every 500ms\n", LED_GPIO);
+ gpio_reset_pin(LED_GPIO);
+ gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
+ for (int count = 0; ; count++) {
+ gpio_set_level(LED_GPIO, count & 1);
+ esp_rom_printf("blink: tick %d\n", count);
+ vTaskDelay(pdMS_TO_TICKS(500));
+ }
+}
diff --git a/tests/projects/embed/esp32/blink/xmake-addons.lua b/tests/projects/embed/esp32/blink/xmake-addons.lua
new file mode 100644
index 000000000..aea4ad480
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/xmake-addons.lua
@@ -0,0 +1,2 @@
+-- the addon is installed automatically when we load this project
+add_addons("esp32-devel")
diff --git a/tests/projects/embed/esp32/blink/xmake.lua b/tests/projects/embed/esp32/blink/xmake.lua
new file mode 100644
index 000000000..3fea2dd12
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/xmake.lua
@@ -0,0 +1,15 @@
+-- the esp32 blink example, it needs the esp32-devel addon
+--
+-- $ xmake f --board=esp32c3
+-- $ xmake
+-- $ xmake install -- flash the image to the board
+-- $ xmake run -- flash it and monitor the serial output
+--
+includes("@addon/esp32-devel/board")
+
+target("blink")
+ add_rules("@addon/esp32-devel/app")
+ add_files("src/*.c")
+
+ -- the led pin of the board, @see boards/<board>/board.h
+ add_includedirs("boards/" .. (get_config("board") or "esp32c3"))
diff --git a/tests/projects/other/bin2c/src/asset.bin b/tests/projects/other/bin2c/src/asset.bin
new file mode 100644
index 000000000..db1331f22
--- /dev/null
+++ b/tests/projects/other/bin2c/src/asset.bin
@@ -0,0 +1 @@
+hello transform! \ No newline at end of file
diff --git a/tests/projects/other/bin2c/src/asset2.bin b/tests/projects/other/bin2c/src/asset2.bin
new file mode 100644
index 000000000..d7ffc4b96
--- /dev/null
+++ b/tests/projects/other/bin2c/src/asset2.bin
@@ -0,0 +1 @@
+hello luafile! \ No newline at end of file
diff --git a/tests/projects/other/bin2c/src/main.c b/tests/projects/other/bin2c/src/main.c
index 74898132c..d85e9934e 100644
--- a/tests/projects/other/bin2c/src/main.c
+++ b/tests/projects/other/bin2c/src/main.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
+#include <string.h>
static unsigned char g_bin_data[] = {
#include "data.bin.h"
@@ -10,6 +11,14 @@ static unsigned char g_ico_data[] = {
#include "xmake.ico.h"
};
+static unsigned char g_asset_data[] = {
+ #include "asset.bin.h"
+};
+
+static unsigned char g_asset2_data[] = {
+ #include "asset2.bin.h"
+};
+
static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf("%s: size: %u bytes\n", name, (unsigned int)size);
if (size == 0) {
@@ -94,5 +103,25 @@ int main(int argc, char** argv)
hexdump("data.bin", g_bin_data, (uint32_t)sizeof(g_bin_data));
printf("\n");
hexdump("xmake.ico", g_ico_data, (uint32_t)sizeof(g_ico_data));
+ printf("\n");
+ hexdump("asset.bin (transformed)", g_asset_data, (uint32_t)sizeof(g_asset_data));
+ printf("\n");
+ hexdump("asset2.bin (transformed by lua file)", g_asset2_data, (uint32_t)sizeof(g_asset2_data));
+
+ // verify the function transform: asset.bin must be the reverse of "hello transform!" (+ zeroend '\0')
+ const char* expected = "!mrofsnart olleh";
+ if (sizeof(g_asset_data) != 17 || memcmp(g_asset_data, expected, 16) != 0) {
+ printf("asset.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset.bin: transform verification ok\n");
+
+ // verify the lua-file transform: asset2.bin must be the reverse of "hello luafile!" (+ zeroend '\0')
+ const char* expected2 = "!elifaul olleh";
+ if (sizeof(g_asset2_data) != 15 || memcmp(g_asset2_data, expected2, 14) != 0) {
+ printf("asset2.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset2.bin: transform verification ok\n");
return 0;
}
diff --git a/tests/projects/other/bin2c/test.lua b/tests/projects/other/bin2c/test.lua
index b57362078..a23bb98a3 100644
--- a/tests/projects/other/bin2c/test.lua
+++ b/tests/projects/other/bin2c/test.lua
@@ -1,3 +1,5 @@
function main(t)
t:build()
+ -- run the target, main.c verifies the transformed asset.bin and exits non-zero on mismatch
+ os.exec("xmake run test")
end
diff --git a/tests/projects/other/bin2c/transform.lua b/tests/projects/other/bin2c/transform.lua
new file mode 100644
index 000000000..54cf1f394
--- /dev/null
+++ b/tests/projects/other/bin2c/transform.lua
@@ -0,0 +1,5 @@
+function main(inputfile, outputfile)
+ import("core.base.bytes")
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+end
diff --git a/tests/projects/other/bin2c/xmake.lua b/tests/projects/other/bin2c/xmake.lua
index b00dedb65..d60aea710 100644
--- a/tests/projects/other/bin2c/xmake.lua
+++ b/tests/projects/other/bin2c/xmake.lua
@@ -4,7 +4,15 @@ target("test")
set_kind("binary")
add_rules("utils.bin2c", {linewidth = 16, extensions = {".bin", ".ico"}})
add_files("src/*.c")
- add_files("src/*.bin")
+ add_files("src/data.bin")
add_files("src/xmake.ico", {nozeroend = true})
+ add_files("src/asset.bin", {transform = function (inputfile, outputfile, opt)
+ import("core.base.bytes")
+ assert(bytes and opt.target)
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+ end})
+ -- transform can also be a lua script file (supported by the project generators)
+ add_files("src/asset2.bin", {transform = path.join(os.projectdir(), "transform.lua")})
diff --git a/tests/projects/other/bin2obj/src/asset.bin b/tests/projects/other/bin2obj/src/asset.bin
new file mode 100644
index 000000000..db1331f22
--- /dev/null
+++ b/tests/projects/other/bin2obj/src/asset.bin
@@ -0,0 +1 @@
+hello transform! \ No newline at end of file
diff --git a/tests/projects/other/bin2obj/src/asset2.bin b/tests/projects/other/bin2obj/src/asset2.bin
new file mode 100644
index 000000000..d7ffc4b96
--- /dev/null
+++ b/tests/projects/other/bin2obj/src/asset2.bin
@@ -0,0 +1 @@
+hello luafile! \ No newline at end of file
diff --git a/tests/projects/other/bin2obj/src/main.c b/tests/projects/other/bin2obj/src/main.c
index bc1d2442f..03c50352d 100644
--- a/tests/projects/other/bin2obj/src/main.c
+++ b/tests/projects/other/bin2obj/src/main.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
+#include <string.h>
extern const uint8_t _binary_data_bin_start[];
extern const uint8_t _binary_data_bin_end[];
@@ -8,30 +9,39 @@ extern const uint8_t _binary_data_bin_end[];
extern const uint8_t _binary_xmake_ico_start[];
extern const uint8_t _binary_xmake_ico_end[];
+extern const uint8_t _binary_asset_bin_start[];
+extern const uint8_t _binary_asset_bin_end[];
+
+extern const uint8_t _binary_asset2_bin_start[];
+extern const uint8_t _binary_asset2_bin_end[];
+
+extern const uint8_t _binary_asset2_bin_start[];
+extern const uint8_t _binary_asset2_bin_end[];
+
static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf("%s: size: %u bytes\n", name, (unsigned int)size);
if (size == 0) {
return;
}
-
+
// if file is larger than 128 bytes, only dump first 64 and last 64 bytes
uint32_t dump_size = size;
uint32_t start_offset = 0;
uint32_t end_offset = 0;
uint32_t skip_size = 0;
-
+
if (size > 128) {
dump_size = 64;
start_offset = 0;
end_offset = size - 64;
skip_size = end_offset - start_offset - dump_size;
}
-
+
// dump start
for (uint32_t offset = start_offset; offset < start_offset + dump_size; offset += 16) {
// print offset
printf("%08x ", (unsigned int)offset);
-
+
// print hex bytes (8 bytes, space, 8 bytes)
for (uint32_t i = 0; i < 16; i++) {
if (offset + i < size) {
@@ -43,7 +53,7 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf(" ");
}
}
-
+
// print ASCII representation
printf(" |");
for (uint32_t i = 0; i < 16 && offset + i < size; i++) {
@@ -52,18 +62,18 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
}
printf("|\n");
}
-
+
// print skip indicator if needed
if (skip_size > 0) {
printf(" ... (skipped %u bytes) ...\n", (unsigned int)skip_size);
}
-
+
// dump end if needed
if (size > 128) {
for (uint32_t offset = end_offset; offset < size; offset += 16) {
// print offset
printf("%08x ", (unsigned int)offset);
-
+
// print hex bytes (8 bytes, space, 8 bytes)
for (uint32_t i = 0; i < 16; i++) {
if (offset + i < size) {
@@ -75,7 +85,7 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf(" ");
}
}
-
+
// print ASCII representation
printf(" |");
for (uint32_t i = 0; i < 16 && offset + i < size; i++) {
@@ -90,9 +100,31 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
int main(int argc, char** argv) {
const uint32_t _binary_data_bin_size = (uint32_t)(_binary_data_bin_end - _binary_data_bin_start);
const uint32_t _binary_xmake_ico_size = (uint32_t)(_binary_xmake_ico_end - _binary_xmake_ico_start);
+ const uint32_t _binary_asset_bin_size = (uint32_t)(_binary_asset_bin_end - _binary_asset_bin_start);
+ const uint32_t _binary_asset2_bin_size = (uint32_t)(_binary_asset2_bin_end - _binary_asset2_bin_start);
hexdump("data.bin", _binary_data_bin_start, _binary_data_bin_size);
printf("\n");
hexdump("xmake.ico", _binary_xmake_ico_start, _binary_xmake_ico_size);
+ printf("\n");
+ hexdump("asset.bin (transformed)", _binary_asset_bin_start, _binary_asset_bin_size);
+ printf("\n");
+ hexdump("asset2.bin (transformed by lua file)", _binary_asset2_bin_start, _binary_asset2_bin_size);
+
+ // verify the function transform: asset.bin must be the reverse of "hello transform!" (+ zeroend '\0')
+ const char* expected = "!mrofsnart olleh";
+ if (_binary_asset_bin_size != 17 || memcmp(_binary_asset_bin_start, expected, 16) != 0) {
+ printf("asset.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset.bin: transform verification ok\n");
+
+ // verify the lua-file transform: asset2.bin must be the reverse of "hello luafile!" (+ zeroend '\0')
+ const char* expected2 = "!elifaul olleh";
+ if (_binary_asset2_bin_size != 15 || memcmp(_binary_asset2_bin_start, expected2, 14) != 0) {
+ printf("asset2.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset2.bin: transform verification ok\n");
return 0;
}
diff --git a/tests/projects/other/bin2obj/test.lua b/tests/projects/other/bin2obj/test.lua
index 7f2050b62..a75aa7e40 100644
--- a/tests/projects/other/bin2obj/test.lua
+++ b/tests/projects/other/bin2obj/test.lua
@@ -1,4 +1,6 @@
function main(t)
t:build()
+ -- run the target, main.c verifies the transformed asset.bin and exits non-zero on mismatch
+ os.exec("xmake run test")
end
diff --git a/tests/projects/other/bin2obj/transform.lua b/tests/projects/other/bin2obj/transform.lua
new file mode 100644
index 000000000..54cf1f394
--- /dev/null
+++ b/tests/projects/other/bin2obj/transform.lua
@@ -0,0 +1,5 @@
+function main(inputfile, outputfile)
+ import("core.base.bytes")
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+end
diff --git a/tests/projects/other/bin2obj/xmake.lua b/tests/projects/other/bin2obj/xmake.lua
index 9541e17ae..d383bb1bb 100644
--- a/tests/projects/other/bin2obj/xmake.lua
+++ b/tests/projects/other/bin2obj/xmake.lua
@@ -6,3 +6,11 @@ target("test")
add_files("src/*.c")
add_files("src/data.bin", {zeroend = true})
add_files("src/xmake.ico", {zeroend = false})
+ add_files("src/asset.bin", {zeroend = true, transform = function (inputfile, outputfile, opt)
+ import("core.base.bytes")
+ assert(bytes and opt.target)
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+ end})
+ -- transform can also be a lua script file (supported by the project generators)
+ add_files("src/asset2.bin", {zeroend = true, transform = path.join(os.projectdir(), "transform.lua")})