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-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-badinclude/xmake-addons.lua2
-rw-r--r--tests/actions/addon/projects/autofetch-badinclude/xmake.lua2
-rw-r--r--tests/actions/addon/projects/autofetch-badname/xmake-addons.lua2
-rw-r--r--tests/actions/addon/projects/autofetch-badname/xmake.lua2
-rw-r--r--tests/actions/addon/projects/autofetch-build/src/main.c14
-rw-r--r--tests/actions/addon/projects/autofetch-build/xmake-addons.lua2
-rw-r--r--tests/actions/addon/projects/autofetch-build/xmake.lua11
-rw-r--r--tests/actions/addon/projects/autofetch/xmake-addons.lua1
-rw-r--r--tests/actions/addon/projects/autofetch/xmake.lua5
-rw-r--r--tests/actions/addon/test.lua450
-rw-r--r--tests/plugins/repository/test.lua118
28 files changed, 590 insertions, 118 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-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-badinclude/xmake-addons.lua b/tests/actions/addon/projects/autofetch-badinclude/xmake-addons.lua
new file mode 100644
index 000000000..6931855ae
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-badinclude/xmake-addons.lua
@@ -0,0 +1,2 @@
+-- this file only declares the addons, it cannot reference them
+includes("@addon/custom-include/check")
diff --git a/tests/actions/addon/projects/autofetch-badinclude/xmake.lua b/tests/actions/addon/projects/autofetch-badinclude/xmake.lua
new file mode 100644
index 000000000..0d94cf61c
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-badinclude/xmake.lua
@@ -0,0 +1,2 @@
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/autofetch-badname/xmake-addons.lua b/tests/actions/addon/projects/autofetch-badname/xmake-addons.lua
new file mode 100644
index 000000000..165325fb7
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-badname/xmake-addons.lua
@@ -0,0 +1,2 @@
+-- the `addon` name is reserved for the addon references
+add_addons("addon")
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..0d94cf61c
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-badname/xmake.lua
@@ -0,0 +1,2 @@
+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-addons.lua b/tests/actions/addon/projects/autofetch-build/xmake-addons.lua
new file mode 100644
index 000000000..2091e3b7f
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-build/xmake-addons.lua
@@ -0,0 +1,2 @@
+-- the addons which this project needs, they are installed automatically when we load it
+add_addons("custom-include", "custom-rule", "custom-toolchain")
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..bad3f8928
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-build/xmake.lua
@@ -0,0 +1,11 @@
+-- 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/xmake-addons.lua b/tests/actions/addon/projects/autofetch/xmake-addons.lua
new file mode 100644
index 000000000..a85e0fe11
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch/xmake-addons.lua
@@ -0,0 +1 @@
+add_addons("custom-include")
diff --git a/tests/actions/addon/projects/autofetch/xmake.lua b/tests/actions/addon/projects/autofetch/xmake.lua
new file mode 100644
index 000000000..4eaca0785
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch/xmake.lua
@@ -0,0 +1,5 @@
+-- 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/test.lua b/tests/actions/addon/test.lua
new file mode 100644
index 000000000..192230b08
--- /dev/null
+++ b/tests/actions/addon/test.lua
@@ -0,0 +1,450 @@
+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)
+ 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 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 in `xmake-addons.lua` 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 in `xmake-addons.lua` 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
+
+-- the addons file only declares the addons, it cannot reference them
+function test_autofetch_invalid(t)
+ for _, name in ipairs({"autofetch-badinclude", "autofetch-badname"}) do
+ _with_project(name, function ()
+ t:require_not(try { function () os.runv("xmake", {"config", "-y"}); return true end })
+ 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
+
+-- 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/plugins/repository/test.lua b/tests/plugins/repository/test.lua
deleted file mode 100644
index 7b1619d5e..000000000
--- a/tests/plugins/repository/test.lua
+++ /dev/null
@@ -1,118 +0,0 @@
-import("core.base.global")
-
--- write a minimal plugin that prints its name when run
-function _write_plugin(dir, name)
- io.writefile(path.join(dir, "xmake.lua"), string.format([[
-task("%s")
- set_category("plugin")
- on_run("main")
- set_menu {usage = "xmake %s", description = "say hello from %s"}
-]], name, name, name))
- io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], name))
-end
-
--- write a plugin package description, the plugin sources are placed in its `src` directory
---
--- plugins in a repository are described as packages, e.g. <repodir>/plugins/<first-letter>/<name>/xmake.lua
-function _write_plugin_package(dir, name)
- io.writefile(path.join(dir, "xmake.lua"), string.format([[
-package("%s")
- set_kind("plugin")
- set_description("say hello from %s")
- set_sourcedir(path.join(os.scriptdir(), "src"))
-]], name, name))
- _write_plugin(path.join(dir, "src"), name)
-end
-
--- create a temporary plugin repository (packages layout: plugins/<first-letter>/<name>) and register it
---
--- @return reponame, names, cleanup
-function _mock_repo(basenames)
- local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
- local reponame = "plugin-test-repo-" .. suffix
- local repodir = os.tmpfile() .. ".plugin-repo"
- local names = {}
- for _, base in ipairs(basenames) do
- local name = base .. "-" .. suffix
- _write_plugin_package(path.join(repodir, "plugins", name:sub(1, 1), name), name)
- table.insert(names, name)
- end
-
- -- register the repository into the cache
- local cachefile = path.join(global.cachedir(), "repository")
- local cache = os.isfile(cachefile) and io.load(cachefile) or {}
- cache.repositories = cache.repositories or {}
- cache.repositories[reponame] = {repodir}
- io.save(cachefile, cache)
-
- local function cleanup()
- for _, name in ipairs(names) do
- os.tryrm(path.join(global.directory(), "plugins", 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(repodir)
- end
- return reponame, names, cleanup
-end
-
--- install a plugin from a repository, by plain name and by repo@name
-function test_install_from_repo(t)
- local reponame, names, cleanup = _mock_repo({"hello"})
- local name = names[1]
-
- -- install by plain name (searched across all repositories)
- os.runv("xmake", {"plugin", "--install", "-y", name})
- t:require(os.iorunv("xmake", {name}):find(name, 1, true))
-
- -- reinstall by repo@name
- os.runv("xmake", {"plugin", "--remove", name})
- os.runv("xmake", {"plugin", "--install", "-y", reponame .. "@" .. name})
- t:require(os.iorunv("xmake", {name}):find(name, 1, true))
-
- os.runv("xmake", {"plugin", "--remove", name})
- cleanup()
-end
-
--- install a plugin from a local directory, then remove it
-function test_install_from_local(t)
- local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
- local name = "hello-local-" .. suffix
- local dir = path.join(os.tmpfile() .. ".plugin-local", name)
- _write_plugin(dir, name)
-
- os.runv("xmake", {"plugin", "--install", dir})
- t:require(os.iorunv("xmake", {name}):find(name, 1, true))
-
- -- the removed plugin should no longer be runnable
- os.runv("xmake", {"plugin", "--remove", name})
- t:require_not(try { function () os.runv("xmake", {name}); return true end })
-
- os.tryrm(path.directory(dir))
-end
-
--- --list shows the built-in, installed and available plugins
-function test_list(t)
- local reponame, names, cleanup = _mock_repo({"hello", "world"})
-
- -- install the first plugin, leave the second only available
- os.runv("xmake", {"plugin", "--install", "-y", names[1]})
- local out = os.iorunv("xmake", {"plugin", "--list"})
- t:require(out:find("the built-in plugins:", 1, true))
- t:require(out:find("project", 1, true))
- t:require(out:find(names[1], 1, true))
- t:require(out:find(names[2], 1, true))
- t:require(out:find("xmake plugin --install " .. names[2], 1, true))
-
- os.runv("xmake", {"plugin", "--remove", names[1]})
- cleanup()
-end
-
--- invalid installs should fail
-function test_install_invalid(t)
- t:require_not(try { function () os.runv("xmake", {"plugin", "--install", "-y", "plugin-test-missing"}); return true end })
- t:require_not(try { function () os.runv("xmake", {"plugin", "--install", "-y", "somerepo@.."}); return true end })
-end