summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-08-09 21:54:48 +0800
committerruki <[email protected]>2026-08-09 21:54:48 +0800
commit1fbaa53ea58f6359b3dcd277c9794f81bd4a7bcf (patch)
tree97167f1076bd30bfb3cd5bfe26210601d6c271b4
parent7caece0cb3ee8ab207130edc17421d604610e686 (diff)
add demo addon
-rw-r--r--tests/actions/addon/demo-addon/README.md5
-rw-r--r--tests/actions/addon/demo-addon/src/includes/check/xmake.lua7
-rw-r--r--tests/actions/addon/demo-addon/src/modules/greeting.lua3
-rw-r--r--tests/actions/addon/demo-addon/src/plugins/demo_hello/main.lua6
-rw-r--r--tests/actions/addon/demo-addon/src/plugins/demo_hello/xmake.lua10
-rw-r--r--tests/actions/addon/demo-addon/src/rules/app/xmake.lua6
-rw-r--r--tests/actions/addon/demo-addon/src/rules/base/xmake.lua4
-rw-r--r--tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c4
-rw-r--r--tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/xmake.lua3
-rw-r--r--tests/actions/addon/demo-addon/src/toolchains/demo/xmake.lua6
-rw-r--r--tests/actions/addon/demo-addon/tests/hello_test.lua1
-rw-r--r--tests/actions/addon/test.lua54
-rw-r--r--xmake/actions/addon/main.lua31
-rw-r--r--xmake/core/package/addon.lua48
-rw-r--r--xmake/core/sandbox/modules/import/core/package/addon.lua33
15 files changed, 192 insertions, 29 deletions
diff --git a/tests/actions/addon/demo-addon/README.md b/tests/actions/addon/demo-addon/README.md
new file mode 100644
index 000000000..8ee255f64
--- /dev/null
+++ b/tests/actions/addon/demo-addon/README.md
@@ -0,0 +1,5 @@
+# demo-addon
+
+A complete addon used by the addon tests, it provides all the payload kinds
+and uses the optional `src` layout: only `src/` is installed, this README and
+the `tests` directory are ours and must not be installed.
diff --git a/tests/actions/addon/demo-addon/src/includes/check/xmake.lua b/tests/actions/addon/demo-addon/src/includes/check/xmake.lua
new file mode 100644
index 000000000..3ba656e64
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/includes/check/xmake.lua
@@ -0,0 +1,7 @@
+print("demo-addon: includes check is loaded")
+
+option("demo_addon_option")
+ set_default(true)
+ set_showmenu(true)
+ set_description("An option provided by the demo addon.")
+option_end()
diff --git a/tests/actions/addon/demo-addon/src/modules/greeting.lua b/tests/actions/addon/demo-addon/src/modules/greeting.lua
new file mode 100644
index 000000000..9adbc01ce
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/modules/greeting.lua
@@ -0,0 +1,3 @@
+function main(who)
+ return "hello from demo-addon: " .. (who or "world")
+end
diff --git a/tests/actions/addon/demo-addon/src/plugins/demo_hello/main.lua b/tests/actions/addon/demo-addon/src/plugins/demo_hello/main.lua
new file mode 100644
index 000000000..c4bf7951c
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/plugins/demo_hello/main.lua
@@ -0,0 +1,6 @@
+import("core.base.option")
+import("@self.greeting")
+
+function main()
+ print(greeting(option.get("name")))
+end
diff --git a/tests/actions/addon/demo-addon/src/plugins/demo_hello/xmake.lua b/tests/actions/addon/demo-addon/src/plugins/demo_hello/xmake.lua
new file mode 100644
index 000000000..8908b23a1
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/plugins/demo_hello/xmake.lua
@@ -0,0 +1,10 @@
+task("demo_hello")
+ set_category("plugin")
+ on_run("main")
+ set_menu {
+ usage = "xmake demo_hello [options]",
+ description = "Say hello from the demo addon.",
+ options = {
+ {'n', "name", "kv", nil, "Set the name to say hello to."}
+ }
+ }
diff --git a/tests/actions/addon/demo-addon/src/rules/app/xmake.lua b/tests/actions/addon/demo-addon/src/rules/app/xmake.lua
new file mode 100644
index 000000000..ff54eb85a
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/rules/app/xmake.lua
@@ -0,0 +1,6 @@
+rule("app")
+ add_deps("@self/base")
+ on_load(function (target)
+ import("@self.greeting")
+ print("demo-addon: rule app is loaded, " .. greeting(target:name()))
+ end)
diff --git a/tests/actions/addon/demo-addon/src/rules/base/xmake.lua b/tests/actions/addon/demo-addon/src/rules/base/xmake.lua
new file mode 100644
index 000000000..0417c2361
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/rules/base/xmake.lua
@@ -0,0 +1,4 @@
+rule("base")
+ on_load(function (target)
+ print("demo-addon: rule base is loaded")
+ end)
diff --git a/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c b/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c
new file mode 100644
index 000000000..e5a9ccbdf
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c
@@ -0,0 +1,4 @@
+int main(int argc, char** argv)
+{
+ return 0;
+}
diff --git a/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/xmake.lua b/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/xmake.lua
new file mode 100644
index 000000000..f9ea6e8d2
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/xmake.lua
@@ -0,0 +1,3 @@
+target("${TARGET_NAME}")
+ set_kind("binary")
+ add_files("src/*.c")
diff --git a/tests/actions/addon/demo-addon/src/toolchains/demo/xmake.lua b/tests/actions/addon/demo-addon/src/toolchains/demo/xmake.lua
new file mode 100644
index 000000000..701a72e29
--- /dev/null
+++ b/tests/actions/addon/demo-addon/src/toolchains/demo/xmake.lua
@@ -0,0 +1,6 @@
+toolchain("demo")
+ set_kind("standalone")
+ set_description("the demo toolchain of the demo addon")
+ on_load(function (toolchain)
+ toolchain:set("toolset", "cc", "gcc")
+ end)
diff --git a/tests/actions/addon/demo-addon/tests/hello_test.lua b/tests/actions/addon/demo-addon/tests/hello_test.lua
new file mode 100644
index 000000000..1fcb312de
--- /dev/null
+++ b/tests/actions/addon/demo-addon/tests/hello_test.lua
@@ -0,0 +1 @@
+-- our own test file, it should not be installed
diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua
index 2be6acf94..1ead7acac 100644
--- a/tests/actions/addon/test.lua
+++ b/tests/actions/addon/test.lua
@@ -406,6 +406,60 @@ function test_install_conflicts(t)
end)
end
+-- a complete addon with the optional `src` layout, @see tests/actions/addon/demo-addon
+--
+-- it provides all the payload kinds and has its own files (README, tests) which must not be installed
+function test_demo_addon(t)
+ local name = "demo-addon"
+ local addondir = path.join(os.scriptdir(), name)
+ try
+ {
+ function ()
+ os.runv("xmake", {"addon", "--install", addondir})
+
+ -- only the payloads of the `src` directory should be installed
+ local installdir = path.join(global.directory(), "addons", name, "latest")
+ for _, payloaddir in ipairs({"plugins", "rules", "toolchains", "modules", "includes", "templates"}) do
+ t:require(os.isdir(path.join(installdir, payloaddir)))
+ end
+ for _, ourfile in ipairs({"src", "tests", "README.md"}) do
+ t:require_not(os.exists(path.join(installdir, ourfile)))
+ end
+
+ -- the plugin imports a module of its own addon with `@self`
+ t:require(os.iorunv("xmake", {"demo_hello", "-n", "xmake"}):find("hello from demo-addon: xmake", 1, true))
+
+ -- the module can be imported with the addon name
+ local script = "import(\"@addon.demo-addon.greeting\"); print(greeting(\"module\"))"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from demo-addon: module", 1, true))
+
+ -- the toolchain can be loaded with the addon name
+ local script2 = "import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/demo-addon/demo\"):get(\"description\"))"
+ t:require(os.iorunv("xmake", {"lua", "-c", script2}):find("the demo toolchain", 1, true))
+
+ -- the template should be listed
+ t:require(os.iorunv("xmake", {"create", "--list", "-l", "c"}):find("demoaddon.hello", 1, true))
+
+ -- the includes and the rules should work in a real project
+ local out = _config_project([[
+includes("@addon/demo-addon/check")
+target("test")
+ set_kind("phony")
+ add_rules("@addon/demo-addon/app")
+]])
+ t:require(out:find("demo-addon: includes check is loaded", 1, true))
+ t:require(out:find("demo-addon: rule base is loaded", 1, true))
+ t:require(out:find("demo-addon: rule app is loaded, hello from demo-addon: test", 1, true))
+ end,
+ finally
+ {
+ function ()
+ try { function () os.runv("xmake", {"addon", "--remove", "--force", name}) end }
+ end
+ }
+ }
+end
+
-- install an addon from a local directory, then remove it
function test_install_from_local(t)
local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
diff --git a/xmake/actions/addon/main.lua b/xmake/actions/addon/main.lua
index c88e6300e..722cdd52d 100644
--- a/xmake/actions/addon/main.lua
+++ b/xmake/actions/addon/main.lua
@@ -78,11 +78,22 @@ end
-- install a single addon from a source directory (as the given name, default to the directory name)
function _install_from_local(dir, name)
assert(os.isdir(dir), "addon path(%s) not found!", dir)
- assert(#addon.payloads_of(dir) > 0, "addon path(%s): no payload directory found, e.g. ${bright}plugins${clear}!", dir)
- name = name or path.filename(path.absolute(dir))
+
+ -- we need to normalize it first, e.g. `/path/to/myaddon/` -> `/path/to/myaddon`,
+ -- otherwise we cannot get the addon name from the directory
+ dir = path.normalize(path.absolute(dir))
+
+ -- we only install the payload directories, the addon repository may have its own files,
+ -- e.g. tests, ci scripts and documents, and they can also be placed in the `src` subdirectory
+ local payloadroot = addon.payloadroot(dir)
+ assert(payloadroot, "addon path(%s): no payload directory found, e.g. ${bright}plugins${clear}!", dir)
+
+ name = name or path.filename(dir)
local dstdir = _get_addondir(name, LOCALVERSION)
assert(not os.isdir(dstdir), "addon(%s) already exists!", name)
- os.vcp(dir, dstdir)
+ for _, payloaddir in ipairs(addon.payloads_of(payloadroot)) do
+ os.vcp(path.join(payloadroot, payloaddir), path.join(dstdir, payloaddir))
+ end
local ok, errors = addon.register(name, LOCALVERSION)
if not ok then
os.tryrm(dstdir)
@@ -127,13 +138,19 @@ function _install_one(name)
return
end
- -- git url or local path
+ -- local directory
+ --
+ -- @note we need to check it before the git url, `git.asgiturl` also accepts
+ -- the local paths with a trailing separator, e.g. `/path/to/myaddon/`
+ if os.isdir(name) then
+ _install_from_local(name)
+ return
+ end
+
+ -- git url
if git.asgiturl(name) then
_install_from_git(name)
return
- elseif os.isdir(name) then
- _install_from_local(name)
- return
end
-- plain name: try to find it in repositories
diff --git a/xmake/core/package/addon.lua b/xmake/core/package/addon.lua
index 7ced8a780..8523da8d0 100644
--- a/xmake/core/package/addon.lua
+++ b/xmake/core/package/addon.lua
@@ -233,6 +233,28 @@ function addon.payloadinfos(kind)
return payloadinfos
end
+-- get the payload root directory of the given addon source directory
+--
+-- an addon repository has its own files, e.g. tests, ci scripts and documents,
+-- so its payloads can be placed in the `src` subdirectory, and we only install them
+--
+-- e.g.
+-- esp32-devel/src/{plugins,rules,toolchains,templates} -- with the `src` layout
+-- hello-world/{plugins} -- without it, for the simple addons
+--
+-- @param sourcedir the addon source directory
+-- @return the payload root directory, it will be nil if no payload is found
+--
+function addon.payloadroot(sourcedir)
+ local srcdir = path.join(sourcedir, "src")
+ if #addon.payloads_of(srcdir) > 0 then
+ return srcdir
+ end
+ if #addon.payloads_of(sourcedir) > 0 then
+ return sourcedir
+ end
+end
+
-- get the payload directories of the given addon directory, e.g. {"plugins", "rules"}
function addon.payloads_of(addondir)
local payloads = {}
@@ -250,16 +272,13 @@ end
--
function addon.installscript()
return function (package)
- local installed = false
- for _, payloaddir in ipairs(addon.payloaddirs()) do
- if os.isdir(payloaddir) then
- os.cp(payloaddir, package:installdir())
- installed = true
- end
- end
- if not installed then
+ local payloadroot = addon.payloadroot(os.curdir())
+ if not payloadroot then
os.raise("addon(%s): no payload directory found, e.g. plugins!", package:name())
end
+ for _, payloaddir in ipairs(addon.payloads_of(payloadroot)) do
+ os.cp(path.join(payloadroot, payloaddir), package:installdir())
+ end
end
end
@@ -414,6 +433,19 @@ function addon.remove(name, opt)
name, table.concat(parents, ", "))
end
end
+ -- we need to remove the symlinks first, we cannot remove them recursively,
+ -- otherwise the linked files would be removed too
+ --
+ -- e.g. the user may link the install directory to the addon source directory when developing it
+ for _, versiondir in ipairs(os.dirs(path.join(installdir, "*"))) do
+ if os.islink(versiondir) then
+ os.rmfile(versiondir)
+ end
+ end
+ if os.islink(installdir) then
+ return os.rmfile(installdir)
+ end
+
local ok, errors = os.rm(installdir)
if not ok then
return false, errors
diff --git a/xmake/core/sandbox/modules/import/core/package/addon.lua b/xmake/core/sandbox/modules/import/core/package/addon.lua
index 2885bd45f..9591559ed 100644
--- a/xmake/core/sandbox/modules/import/core/package/addon.lua
+++ b/xmake/core/sandbox/modules/import/core/package/addon.lua
@@ -25,20 +25,25 @@ local sandbox_core_package_addon = sandbox_core_package_addon or {}
local addon = require("package/addon")
-- inherit some builtin interfaces
-sandbox_core_package_addon.installdir = addon.installdir
-sandbox_core_package_addon.dirname = addon.dirname
-sandbox_core_package_addon.registryfile = addon.registryfile
-sandbox_core_package_addon.payloaddirs = addon.payloaddirs
-sandbox_core_package_addon.payloads = addon.payloads
-sandbox_core_package_addon.payloadinfos = addon.payloadinfos
-sandbox_core_package_addon.payloads_of = addon.payloads_of
-sandbox_core_package_addon.addons = addon.addons
-sandbox_core_package_addon.addondir = addon.addondir
-sandbox_core_package_addon.register = addon.register
-sandbox_core_package_addon.unregister = addon.unregister
-sandbox_core_package_addon.remove = addon.remove
-sandbox_core_package_addon.rescan = addon.rescan
-sandbox_core_package_addon.clear = addon.clear
+sandbox_core_package_addon.installdir = addon.installdir
+sandbox_core_package_addon.dirname = addon.dirname
+sandbox_core_package_addon.owner = addon.owner
+sandbox_core_package_addon.is_reference = addon.is_reference
+sandbox_core_package_addon.resolve_reference = addon.resolve_reference
+sandbox_core_package_addon.registryfile = addon.registryfile
+sandbox_core_package_addon.payloaddirs = addon.payloaddirs
+sandbox_core_package_addon.payloads = addon.payloads
+sandbox_core_package_addon.payloaddir = addon.payloaddir
+sandbox_core_package_addon.payloadinfos = addon.payloadinfos
+sandbox_core_package_addon.payloads_of = addon.payloads_of
+sandbox_core_package_addon.payloadroot = addon.payloadroot
+sandbox_core_package_addon.addons = addon.addons
+sandbox_core_package_addon.addondir = addon.addondir
+sandbox_core_package_addon.register = addon.register
+sandbox_core_package_addon.unregister = addon.unregister
+sandbox_core_package_addon.remove = addon.remove
+sandbox_core_package_addon.rescan = addon.rescan
+sandbox_core_package_addon.clear = addon.clear
-- return module
return sandbox_core_package_addon