diff options
| author | Saikari <[email protected]> | 2026-07-10 22:28:38 +0300 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-07-30 10:17:31 +0800 |
| commit | 4b6a2eb23d0903124871f837798db2dd718e86a1 (patch) | |
| tree | c0dda814d7b199e1c54db61803c4051f4a834a3b /xmake/modules | |
| parent | a928217681c4c5dc60b80a230182e5254bc30c8d (diff) | |
feat: add support for plugin packages in xmake
Diffstat (limited to 'xmake/modules')
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() |
