summaryrefslogtreecommitdiff
path: root/xmake/core/package/package.lua
diff options
context:
space:
mode:
authorruki <[email protected]>2026-08-15 17:57:22 +0800
committerGitHub <[email protected]>2026-08-15 17:57:22 +0800
commit0a32ec18d737259052271fa3be5cfdb50dbd1d15 (patch)
tree50be742aed502e6f5432ff841766bec4e38ff16d /xmake/core/package/package.lua
parent069474769dcfb01898f2c7066c1bf71d5da79125 (diff)
parent67ffcd8a481a5c6c51da8044bfe1ebed980b1e56 (diff)
Merge pull request #7696 from xmake-io/addon
Add addon support for plugins
Diffstat (limited to 'xmake/core/package/package.lua')
-rw-r--r--xmake/core/package/package.lua106
1 files changed, 101 insertions, 5 deletions
diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua
index e014d9578..78268351e 100644
--- a/xmake/core/package/package.lua
+++ b/xmake/core/package/package.lua
@@ -37,6 +37,7 @@ local interpreter = require("base/interpreter")
local select_script = require("base/private/select_script")
local is_cross = require("base/private/is_cross")
local memcache = require("cache/memcache")
+local addon = require("package/addon")
local toolchain = require("tool/toolchain")
local compiler = require("tool/compiler")
local linker = require("tool/linker")
@@ -620,7 +621,19 @@ function _instance:is_toolchain()
return self:kind() == "toolchain"
end
+-- is addon package?
+--
+-- it will be installed to `~/.xmake/addons/<name>/<version>` and
+-- it can provide plugins, rules, toolchains, templates and modules for xmake
+--
+function _instance:is_addon()
+ return self:kind() == "addon"
+end
+
-- is plugin package?
+--
+-- @note this kind is deprecated, please use the `addon` kind instead
+--
function _instance:is_plugin()
return self:kind() == "plugin"
end
@@ -922,7 +935,15 @@ function _instance:installdir(...)
installdir = self:get("installdir")
if not installdir then
local name = self:name():lower():gsub("::", "_")
- if self:is_plugin() then
+ if self:is_addon() then
+ -- e.g. ~/.xmake/addons/<name>/<version>
+ local version_str = self:version_str() or "latest"
+ if os.is_host("windows") then
+ version_str = version_str:gsub("[>=<|%*]", "")
+ end
+ installdir = addon.addondir(self:name(), version_str)
+ elseif self:is_plugin() then
+ -- deprecated, @see the `addon` kind
installdir = path.join(global.directory(), "plugins", name)
else
if self:is_local() then
@@ -1194,10 +1215,6 @@ function _instance:_rawenvs()
end
end
- -- add plugin env for on_test
- if self:is_plugin() then
- envs.XMAKE_PLUGIN_DIRS = path.directory(self:installdir())
- end
self._RAWENVS = envs
end
return envs
@@ -2580,6 +2597,79 @@ function _instance:_generate_build_configs(configs, opt)
return configs
end
+-- has the given payloads? (only for the addon packages)
+--
+-- @param opt the payloads to be checked, the value can be a string or a list, e.g.
+-- {rules = "app", toolchains = "esp32", plugins = "monitor",
+-- templates = "c/esp32.hello", modules = {"private.board", "private.flasher"}}
+--
+-- @return true, or false and errors
+--
+-- e.g.
+--
+-- on_test(function (package)
+-- assert(package:has_addon({rules = "app", toolchains = "esp32"}))
+-- end)
+--
+function _instance:has_addon(opt)
+ if not self:is_addon() then
+ return false, string.format("package(%s) is not an addon!", self:name())
+ end
+
+ -- the addon should be registered after installing it
+ local addoninfo = addon.addons()[addon.dirname(self:name())]
+ if not addoninfo then
+ return false, string.format("addon(%s) is not installed!", self:name())
+ end
+ if opt == nil then
+ return true
+ end
+
+ local checkers = {}
+
+ -- the rules are namespaced, e.g. @addon/esp32-devel/app
+ --
+ -- @note we need to reload the global rules, this addon may be installed just now
+ checkers.rules = function (name)
+ local rule = require("project/rule")
+ rule.clear()
+ return rule.rules()["@addon/" .. self:name() .. "/" .. name] ~= nil
+ end
+
+ -- the toolchains are namespaced too, e.g. @addon/esp32-devel/esp32
+ checkers.toolchains = function (name)
+ return toolchain.load("@addon/" .. self:name() .. "/" .. name) ~= nil
+ end
+
+ -- the plugins and the templates are not namespaced, we get them from the addons registry,
+ -- the task list of this process has been loaded before installing this addon
+ checkers.plugins = function (name)
+ return table.contains(addoninfo.plugins or {}, name)
+ end
+ checkers.templates = function (name)
+ return table.contains(addoninfo.templates or {}, name)
+ end
+
+ -- the modules are files, e.g. modules/serial.lua, modules/private/board.lua
+ checkers.modules = function (name)
+ local modulepath = path.join(self:installdir(), "modules", (name:gsub("%.", "/")))
+ return os.isfile(modulepath .. ".lua") or os.isdir(modulepath)
+ end
+
+ for kind, names in pairs(opt) do
+ local checker = checkers[kind]
+ if not checker then
+ return false, string.format("unknown addon payload(%s), it should be one of rules, toolchains, plugins, templates and modules!", kind)
+ end
+ for _, name in ipairs(table.wrap(names)) do
+ if not checker(name) then
+ return false, string.format("%s(%s) not found in the addon(%s)!", kind, name, self:name())
+ end
+ end
+ end
+ return true
+end
+
-- has the given c funcs?
--
-- @param funcs the funcs
@@ -3226,7 +3316,13 @@ function package.load_from_repository(packagename, packagedir, opt)
return nil, string.format("%s: package(%s) not found!", scriptpath, packagename)
end
+ -- we need set the default on_install script if it's addon package
+ if packageinfo:get("kind") == "addon" and not packageinfo:get("install") then
+ packageinfo:set("install", addon.installscript())
+ end
+
-- we need set the default on_install script if it's plugin package
+ -- @note the plugin kind is deprecated, please use the addon kind instead
if packageinfo:get("kind") == "plugin" and not packageinfo:get("install") then
-- only one code line, we can directly omit the sandbox wrapper.
local on_install = function (pkg)