summaryrefslogtreecommitdiff
path: root/xmake/modules
diff options
context:
space:
mode:
authorSaikari <[email protected]>2026-07-10 22:28:38 +0300
committerruki <[email protected]>2026-07-30 10:17:31 +0800
commit4b6a2eb23d0903124871f837798db2dd718e86a1 (patch)
treec0dda814d7b199e1c54db61803c4051f4a834a3b /xmake/modules
parenta928217681c4c5dc60b80a230182e5254bc30c8d (diff)
feat: add support for plugin packages in xmake
Diffstat (limited to 'xmake/modules')
-rw-r--r--xmake/modules/package/manager/xmake/search_package.lua52
-rw-r--r--xmake/modules/private/action/require/impl/actions/install.lua12
-rw-r--r--xmake/modules/private/action/require/impl/package.lua1
-rw-r--r--xmake/modules/private/action/require/impl/remove_packages.lua6
-rw-r--r--xmake/modules/private/action/require/impl/repository.lua42
-rw-r--r--xmake/modules/private/action/require/impl/uninstall_packages.lua5
-rw-r--r--xmake/modules/private/action/require/impl/utils/plugins.lua51
-rw-r--r--xmake/modules/private/action/require/search.lua18
-rw-r--r--xmake/modules/private/check/checkers/api/package/kind.lua2
-rw-r--r--xmake/modules/private/xrepo/action/install.lua27
-rw-r--r--xmake/modules/private/xrepo/action/remove.lua13
-rw-r--r--xmake/modules/private/xrepo/action/search.lua15
-rw-r--r--xmake/modules/private/xrepo/quick_search/cache.lua14
13 files changed, 218 insertions, 40 deletions
diff --git a/xmake/modules/package/manager/xmake/search_package.lua b/xmake/modules/package/manager/xmake/search_package.lua
index 32991ae3e..04d69d135 100644
--- a/xmake/modules/package/manager/xmake/search_package.lua
+++ b/xmake/modules/package/manager/xmake/search_package.lua
@@ -27,31 +27,43 @@ function _search_package(packages, name, opt)
local packagename = packageinfo.name
local packagedata = packageinfo.data
- local version
- local versions = packagedata.versions
- if versions then
- versions = table.copy(versions)
- table.sort(versions, function (a, b) return semver.compare(a, b) > 0 end)
- if opt.require_version then
- for _, ver in ipairs(versions) do
- if semver.satisfies(ver, opt.require_version) then
- version = ver
+ -- only search the packages with the given kind, e.g. plugin,
+ -- and we will ignore the plugin packages by default
+ local kind_matched
+ if opt.kind then
+ kind_matched = packagedata.kind == opt.kind
+ else
+ kind_matched = packagedata.kind ~= "plugin"
+ end
+ if kind_matched then
+
+ local version
+ local versions = packagedata.versions
+ if versions then
+ versions = table.copy(versions)
+ table.sort(versions, function (a, b) return semver.compare(a, b) > 0 end)
+ if opt.require_version then
+ for _, ver in ipairs(versions) do
+ if semver.satisfies(ver, opt.require_version) then
+ version = ver
+ end
end
+ else
+ version = versions[1]
end
- else
- version = versions[1]
end
- end
- local description = packagedata.description
- if description then
- description = description:gsub(string.ipattern(name), function (w)
- return "${bright}" .. w .. "${clear}"
- end)
- end
+ local description = packagedata.description
+ -- do not highlight the description if the pattern starts with a quantifier, e.g. `xrepo search -k plugin "*"`
+ if description and not name:startswith("*") and not name:startswith("+") then
+ description = description:gsub(string.ipattern(name), function (w)
+ return "${bright}" .. w .. "${clear}"
+ end)
+ end
- if not opt.require_version or version then
- packages[packagename] = {name = packagename, version = version, description = description, reponame = packagedata.reponame}
+ if not opt.require_version or version then
+ packages[packagename] = {name = packagename, version = version, description = description, reponame = packagedata.reponame, kind = packagedata.kind}
+ end
end
end
end
diff --git a/xmake/modules/private/action/require/impl/actions/install.lua b/xmake/modules/private/action/require/impl/actions/install.lua
index 7fb54a951..3d1c15621 100644
--- a/xmake/modules/private/action/require/impl/actions/install.lua
+++ b/xmake/modules/private/action/require/impl/actions/install.lua
@@ -32,6 +32,7 @@ import("private.action.require.impl.actions.test")
import("private.action.require.impl.actions.patch_sources")
import("private.action.require.impl.actions.download_resources")
import("private.action.require.impl.utils.filter")
+import("private.action.require.impl.utils.plugins")
-- patch pkgconfig if not exists
function _patch_pkgconfig(package)
@@ -513,6 +514,12 @@ function main(package)
end
assert(fetchinfo, "fetch %s failed!", package_tipname)
+ -- register the plugin package to the global plugins directory,
+ -- we need to register it before testing, because the test script may run this plugin task.
+ if package:is_plugin() then
+ plugins.register(package)
+ end
+
-- this package is installed now
if installed_now then
@@ -557,6 +564,11 @@ function main(package)
-- leave the package environments
os.setenvs(oldenvs)
+ -- unregister the broken plugin package
+ if package:is_plugin() then
+ plugins.unregister(package:name())
+ end
+
-- copy the invalid package directory to cache
local installdir = package:installdir()
if os.isdir(installdir) then
diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua
index 3ad758c3a..a3b58a0bd 100644
--- a/xmake/modules/private/action/require/impl/package.lua
+++ b/xmake/modules/private/action/require/impl/package.lua
@@ -949,6 +949,7 @@ function _load_package(packagename, requireinfo, opt)
plat = requireinfo.plat,
arch = requireinfo.arch,
name = requireinfo.reponame,
+ kind = requireinfo.kind,
locked_repo = locked_requireinfo and locked_requireinfo.repo})
if package then
from_repo = true
diff --git a/xmake/modules/private/action/require/impl/remove_packages.lua b/xmake/modules/private/action/require/impl/remove_packages.lua
index a10fc803d..dfe50c3a4 100644
--- a/xmake/modules/private/action/require/impl/remove_packages.lua
+++ b/xmake/modules/private/action/require/impl/remove_packages.lua
@@ -23,6 +23,7 @@ import("core.base.option")
import("core.base.hashset")
import("core.package.package")
import("core.cache.localcache")
+import("private.action.require.impl.utils.plugins")
-- get package configs string
function _get_package_configs_str(manifest_file)
@@ -94,7 +95,12 @@ function _remove_packagedirs(packagedir, opt)
local description = string.format("remove ${color.dump.string}%s-%s${clear}/${yellow}%s${clear}\n -> ${dim}%s${clear} (${red}%s${clear})", package_name, version, hash, configs_str, status and status or "used")
local confirm = utils.confirm({default = true, description = description})
if confirm then
+ local manifest = os.isfile(manifest_file) and io.load(manifest_file)
os.rm(hashdir)
+ -- unregister the plugin package from the global plugins directory
+ if manifest and manifest.kind == "plugin" then
+ plugins.unregister(manifest.name or package_name)
+ end
end
end
end
diff --git a/xmake/modules/private/action/require/impl/repository.lua b/xmake/modules/private/action/require/impl/repository.lua
index 3df59c42c..e2ac794df 100644
--- a/xmake/modules/private/action/require/impl/repository.lua
+++ b/xmake/modules/private/action/require/impl/repository.lua
@@ -27,8 +27,36 @@ import("core.package.repository")
import("devel.git")
import("net.proxy")
+-- find the package directory in the given repository directory
+--
+-- the layout of repository:
+--
+-- packages/z/zlib/xmake.lua
+-- plugins/hello/xmake.lua
+-- plugins/h/hello/xmake.lua
+--
+function _find_packagedir(repodir, packagename, opt)
+ opt = opt or {}
+ local packagedirs = {path.join("packages", packagename:sub(1, 1), packagename)}
+ local plugindirs = {path.join("plugins", packagename),
+ path.join("plugins", packagename:sub(1, 1), packagename)}
+ local dirs
+ if opt.kind == "plugin" then
+ -- find it from the plugin directories first
+ dirs = table.join(plugindirs, packagedirs)
+ else
+ dirs = table.join(packagedirs, plugindirs)
+ end
+ for _, dir in ipairs(dirs) do
+ dir = path.join(repodir, dir)
+ if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then
+ return dir
+ end
+ end
+end
+
-- get package directory from the locked repository
-function _get_packagedir_from_locked_repo(packagename, locked_repo)
+function _get_packagedir_from_locked_repo(packagename, locked_repo, opt)
-- find global repository directory
local repo_global
@@ -101,8 +129,8 @@ function _get_packagedir_from_locked_repo(packagename, locked_repo)
-- find package directory
local foundir
if ok then
- local dir = path.join(repodir_local, "packages", packagename:sub(1, 1), packagename)
- if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then
+ local dir = _find_packagedir(repodir_local, packagename, opt)
+ if dir then
local repo = repository.load(reponame, locked_repo.url, locked_repo.branch, false)
foundir = {dir, repo}
vprint("lock package(%s) in %s from repository(%s)/%s", packagename, dir, locked_repo.url, locked_repo.commit)
@@ -162,7 +190,7 @@ function packagedir(packagename, opt)
-- get cache key
local reponame = opt.name
- local cachekey = packagename
+ local cachekey = packagename .. (opt.kind or "")
local locked_repo = opt.locked_repo
if locked_repo then
cachekey = cachekey .. locked_repo.url .. (locked_repo.commit or "") .. (locked_repo.branch or "")
@@ -179,14 +207,14 @@ function packagedir(packagename, opt)
-- find the package directory from the locked repository
if locked_repo then
- foundir = _get_packagedir_from_locked_repo(packagename, locked_repo)
+ foundir = _get_packagedir_from_locked_repo(packagename, locked_repo, opt)
end
-- find the package directory from repositories
if not foundir then
for _, repo in ipairs(repositories()) do
- local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1), packagename)
- if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) and (not reponame or reponame == repo:name()) then
+ local dir = _find_packagedir(repo:directory(), packagename, opt)
+ if dir and (not reponame or reponame == repo:name()) then
foundir = {dir, repo}
break
end
diff --git a/xmake/modules/private/action/require/impl/uninstall_packages.lua b/xmake/modules/private/action/require/impl/uninstall_packages.lua
index 685b5c973..f5434fb17 100644
--- a/xmake/modules/private/action/require/impl/uninstall_packages.lua
+++ b/xmake/modules/private/action/require/impl/uninstall_packages.lua
@@ -21,6 +21,7 @@
-- imports
import("core.cache.localcache")
import("private.action.require.impl.package")
+import("private.action.require.impl.utils.plugins")
-- uninstall packages
-- uninstall required packages
@@ -46,6 +47,10 @@ function main(requires, opt)
table.insert(packages, instance)
end
os.tryrm(instance:installdir())
+ -- unregister the plugin package from the global plugins directory
+ if instance:is_plugin() then
+ plugins.unregister(instance:name())
+ end
end
return packages
end
diff --git a/xmake/modules/private/action/require/impl/utils/plugins.lua b/xmake/modules/private/action/require/impl/utils/plugins.lua
new file mode 100644
index 000000000..9cd7c5d86
--- /dev/null
+++ b/xmake/modules/private/action/require/impl/utils/plugins.lua
@@ -0,0 +1,51 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file plugins.lua
+--
+
+-- imports
+import("core.base.global")
+
+-- get the plugin directory of the given plugin package in the global plugins directory
+function plugindir(name)
+ return path.join(global.directory(), "plugins", name)
+end
+
+-- register the given plugin package to the global plugins directory,
+-- then we can run it directly. e.g. `xmake plugin-name`
+function register(package)
+ local installdir = package:installdir()
+ assert(os.isfile(path.join(installdir, "xmake.lua")),
+ "plugin(%s): xmake.lua not found in the installed files, it should be installed with `os.cp(\"*\", package:installdir())`!", package:name())
+ local dir = plugindir(package:name())
+ os.tryrm(dir)
+ os.cp(installdir, dir)
+ -- remove the install logs, they do not belong to the plugin,
+ -- but we keep manifest.txt to show the plugin version and description. e.g. `xmake plugin --list`
+ os.tryrm(path.join(dir, "logs"))
+ vprint("register plugin(%s) to %s", package:name(), dir)
+end
+
+-- unregister the given plugin from the global plugins directory
+function unregister(name)
+ local dir = plugindir(name)
+ if os.isdir(dir) then
+ os.tryrm(dir)
+ vprint("unregister plugin(%s) from %s", name, dir)
+ end
+end
diff --git a/xmake/modules/private/action/require/search.lua b/xmake/modules/private/action/require/search.lua
index e714c6c19..75ebd8d50 100644
--- a/xmake/modules/private/action/require/search.lua
+++ b/xmake/modules/private/action/require/search.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.task")
+import("core.base.option")
import("private.action.require.impl.utils.filter")
import("private.action.require.impl.repository")
import("private.action.require.impl.environment")
@@ -41,11 +42,22 @@ function main(names)
task.run("repo", {update = true})
end
+ -- get the extra search options, e.g. --extra="{kind='plugin'}"
+ local opt = {}
+ local extra = option.get("extra")
+ if extra then
+ local extrainfo, errors = string.deserialize(extra)
+ if not extrainfo then
+ raise(errors)
+ end
+ opt.kind = extrainfo.kind
+ end
+
-- show title
print("The package names:")
-- search packages
- for name, packages in pairs(search_packages(names)) do
+ for name, packages in pairs(search_packages(names, opt)) do
if #packages > 0 then
-- show name
@@ -57,8 +69,10 @@ function main(names)
local version = result.version
local reponame = result.reponame
local description = result.description
- cprint(" -> ${color.dump.string}%s%s${clear}: %s %s", name,
+ local kind = result.kind
+ cprint(" -> ${color.dump.string}%s%s${clear}%s: %s %s", name,
version and ("-" .. version) or "",
+ kind == "plugin" and " ${magenta}(plugin)${clear}" or "",
description or "",
reponame and ("(in " .. reponame .. ")") or "")
end
diff --git a/xmake/modules/private/check/checkers/api/package/kind.lua b/xmake/modules/private/check/checkers/api/package/kind.lua
index 4a9c309e3..d2c7afd10 100644
--- a/xmake/modules/private/check/checkers/api/package/kind.lua
+++ b/xmake/modules/private/check/checkers/api/package/kind.lua
@@ -37,6 +37,6 @@ function main(opt)
end
return true
end
- return value == "binary" or value == "toolchain" or value == "template"
+ return value == "binary" or value == "toolchain" or value == "template" or value == "plugin"
end}))
end
diff --git a/xmake/modules/private/xrepo/action/install.lua b/xmake/modules/private/xrepo/action/install.lua
index b0d297b38..6b38bca0f 100644
--- a/xmake/modules/private/xrepo/action/install.lua
+++ b/xmake/modules/private/xrepo/action/install.lua
@@ -30,8 +30,8 @@ function menu_options()
-- menu options
local options =
{
- {'k', "kind", "kv", nil, "Enable static/shared library.",
- values = {"static", "shared"} },
+ {'k', "kind", "kv", nil, "Enable static/shared library or install plugin package.",
+ values = {"static", "shared", "plugin"}},
{'p', "plat", "kv", nil, "Set the given platform." },
{'a', "arch", "kv", nil, "Set the given architecture." },
{'m', "mode", "kv", nil, "Set the given mode.",
@@ -47,6 +47,10 @@ function menu_options()
"e.g.",
" - xrepo install -p cross --toolchain=mytool --includes='toolchain1.lua" .. path.envsep() .. "toolchain2.lua'"},
{nil, "policies", "kv", nil, "Set the policies." },
+ {nil, "repo", "kv", nil, "Set the given repository.",
+ "e.g.",
+ " - xrepo install --repo=my-repo zlib",
+ " - xrepo install -k plugin --repo=my-repo hello-world"},
{category = "Visual Studio SDK Configuration" },
{nil, "vs", "kv", nil, "The Microsoft Visual Studio"
, " e.g. --vs=2017" },
@@ -93,6 +97,7 @@ function menu_options()
" - xrepo install -p android [--ndk=/xxx] -m debug \"pcre2 10.x\"",
" - xrepo install -p mingw [--mingw=/xxx] -k shared zlib",
" - xrepo install conan::zlib/1.2.11 vcpkg::zlib",
+ " - xrepo install -k plugin hello-world",
values = function (complete, opt) return import("private.xrepo.quick_search.completion")(complete, opt) end}
}
@@ -178,7 +183,7 @@ function _install_packages(packages)
table.insert(config_argv, mode)
end
local kind = option.get("kind")
- if kind then
+ if kind and kind ~= "plugin" then
table.insert(config_argv, "-k")
table.insert(config_argv, kind)
end
@@ -285,7 +290,9 @@ function _install_packages(packages)
if mode == "debug" then
extra.debug = true
end
- if kind then
+ if kind == "plugin" then
+ extra.kind = "plugin"
+ elseif kind then
extra.configs = extra.configs or {}
extra.configs.shared = kind == "shared"
end
@@ -306,6 +313,18 @@ function _install_packages(packages)
local extra_str = string.serialize(extra, {indent = false, strip = true})
table.insert(require_argv, "--extra=" .. extra_str)
end
+ -- install the packages from the given repository, e.g. xrepo install --repo=my-repo zlib
+ local repo = option.get("repo")
+ if repo then
+ local result = {}
+ for _, name in ipairs(packages) do
+ if not name:find("@", 1, true) and not name:find("::", 1, true) then
+ name = repo .. "@" .. name
+ end
+ table.insert(result, name)
+ end
+ packages = result
+ end
table.join2(require_argv, packages)
end
os.vexecv(os.programfile(), require_argv, {envs = envs})
diff --git a/xmake/modules/private/xrepo/action/remove.lua b/xmake/modules/private/xrepo/action/remove.lua
index ed92a83b8..b59f6b7b0 100644
--- a/xmake/modules/private/xrepo/action/remove.lua
+++ b/xmake/modules/private/xrepo/action/remove.lua
@@ -31,8 +31,8 @@ function menu_options()
-- menu options
local options =
{
- {'k', "kind", "kv", nil, "Enable static/shared library.",
- values = {"static", "shared"} },
+ {'k', "kind", "kv", nil, "Enable static/shared library or remove plugin package.",
+ values = {"static", "shared", "plugin"} },
{'p', "plat", "kv", nil, "Set the given platform." },
{'a', "arch", "kv", nil, "Set the given architecture." },
{'m', "mode", "kv", nil, "Set the given mode.",
@@ -56,7 +56,8 @@ function menu_options()
" - xrepo remove -p iphoneos -a arm64 \"zlib >=1.2.0\"",
" - xrepo remove -p android -m debug \"pcre2 10.x\"",
" - xrepo remove -p mingw -k shared zlib",
- " - xrepo remove conan::zlib/1.2.11 vcpkg::zlib" }
+ " - xrepo remove conan::zlib/1.2.11 vcpkg::zlib",
+ " - xrepo remove -k plugin hello-world" }
}
-- show menu options
@@ -137,7 +138,7 @@ function _remove_packages(packages)
table.insert(config_argv, mode)
end
local kind = option.get("kind")
- if kind then
+ if kind and kind ~= "plugin" then
table.insert(config_argv, "-k")
table.insert(config_argv, kind)
end
@@ -168,7 +169,9 @@ function _remove_packages(packages)
if mode == "debug" then
extra.debug = true
end
- if kind then
+ if kind == "plugin" then
+ extra.kind = "plugin"
+ elseif kind then
extra.configs = extra.configs or {}
extra.configs.shared = kind == "shared"
end
diff --git a/xmake/modules/private/xrepo/action/search.lua b/xmake/modules/private/xrepo/action/search.lua
index d389e3833..ff379c5db 100644
--- a/xmake/modules/private/xrepo/action/search.lua
+++ b/xmake/modules/private/xrepo/action/search.lua
@@ -30,10 +30,14 @@ function menu_options()
-- menu options
local options =
{
+ {'k', "kind", "kv", nil, "Set the package kind to be searched.",
+ values = {"plugin"} },
{nil, "packages", "vs", nil, "The packages list (support lua pattern).",
"e.g.",
" - xrepo search zlib boost",
- " - xrepo search \"pcre*\""}
+ " - xrepo search \"pcre*\"",
+ " - xrepo search -k plugin",
+ " - xrepo search -k plugin hello-world"}
}
-- show menu options
@@ -80,6 +84,11 @@ function _search_packages(packages)
if option.get("diagnosis") then
table.insert(require_argv, "-D")
end
+ local kind = option.get("kind")
+ if kind then
+ local extra_str = string.serialize({kind = kind}, {indent = false, strip = true})
+ table.insert(require_argv, "--extra=" .. extra_str)
+ end
table.join2(require_argv, packages)
os.vexecv(os.programfile(), require_argv)
end
@@ -87,6 +96,10 @@ end
-- main entry
function main()
local packages = option.get("packages")
+ if not packages and option.get("kind") then
+ -- list all packages with the given kind, e.g. xrepo search -k plugin
+ packages = {"*"}
+ end
if packages then
_search_packages(packages)
else
diff --git a/xmake/modules/private/xrepo/quick_search/cache.lua b/xmake/modules/private/xrepo/quick_search/cache.lua
index cf75a212d..ee0eacc67 100644
--- a/xmake/modules/private/xrepo/quick_search/cache.lua
+++ b/xmake/modules/private/xrepo/quick_search/cache.lua
@@ -42,6 +42,19 @@ function _list_package_dirs()
end
end
end
+ -- find the plugin package directories, e.g. plugins/hello/xmake.lua, plugins/h/hello/xmake.lua
+ for _, file in ipairs(table.join(os.files(path.join(repo:directory(), "plugins", "*", "xmake.lua")),
+ os.files(path.join(repo:directory(), "plugins", "*", "*", "xmake.lua")))) do
+ local dir = path.directory(file)
+ local subdirname = path.basename(path.directory(dir))
+ if subdirname == "plugins" or #subdirname == 1 then
+ local packagename = path.filename(dir)
+ if not unique[packagename] then
+ table.insert(packageinfos, {name = packagename, repo = repo, packagedir = dir})
+ unique[packagename] = true
+ end
+ end
+ end
end
return packageinfos
end
@@ -61,6 +74,7 @@ function update()
reponame = package:repo() and package:repo():name(),
description = package:description(),
versions = package:versions(),
+ kind = package:kind(),
})
end
cache:save()