From 4b6a2eb23d0903124871f837798db2dd718e86a1 Mon Sep 17 00:00:00 2001 From: Saikari Date: Fri, 10 Jul 2026 22:28:38 +0300 Subject: feat: add support for plugin packages in xmake --- xmake/plugins/plugin/xmake.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index 05d9e3631..b61dd05f9 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -27,10 +27,16 @@ task("plugin") options = { {'i', "install", "k", nil, "Install plugins."}, {'u', "update", "k", nil, "Update plugins."}, + {'r', "remove", "k", nil, "Remove the given installed plugin."}, + {'l', "list", "k", nil, "List all installed plugins."}, {'c', "clear", "k", nil, "Clear all installed plugins."}, - {nil, "plugins", "v", nil, "The plugins path or url.", + {nil, "plugins", "v", nil, "The plugins path, url or package name.", "e.g.", " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins", - " $ xmake plugin --update"} + " $ xmake plugin --install hello-world", + " $ xmake plugin --remove hello-world", + " $ xmake plugin --list", + " $ xmake plugin --update", + " $ xmake plugin --update hello-world"} } } -- cgit v1.3.1 From 21f1b0efbf5441e5ec82a2caa84984a77ad7e7eb Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 26 Jul 2026 22:50:42 +0300 Subject: Added tests for plugins --- tests/plugins/repository/test.lua | 56 ++++ xmake/core/base/task.lua | 21 +- xmake/core/package/package.lua | 26 +- .../package/manager/xmake/search_package.lua | 52 ++-- .../action/require/impl/actions/install.lua | 14 - .../private/action/require/impl/package.lua | 1 - .../action/require/impl/remove_packages.lua | 6 - .../private/action/require/impl/repository.lua | 55 +--- .../action/require/impl/uninstall_packages.lua | 5 - .../private/action/require/impl/utils/plugins.lua | 81 ------ xmake/modules/private/action/require/search.lua | 17 +- .../private/check/checkers/api/package/kind.lua | 2 +- xmake/modules/private/xrepo/action/install.lua | 37 +-- xmake/modules/private/xrepo/action/remove.lua | 23 +- xmake/modules/private/xrepo/action/search.lua | 15 +- xmake/modules/private/xrepo/quick_search/cache.lua | 27 +- .../private/xrepo/quick_search/completion.lua | 7 +- xmake/plugins/plugin/main.lua | 114 ++++---- xmake/plugins/plugin/xmake.lua | 3 +- xmake/plugins/xfetch/main.lua | 289 --------------------- xmake/plugins/xfetch/xmake.lua | 32 --- 21 files changed, 181 insertions(+), 702 deletions(-) create mode 100644 tests/plugins/repository/test.lua delete mode 100644 xmake/modules/private/action/require/impl/utils/plugins.lua delete mode 100644 xmake/plugins/xfetch/main.lua delete mode 100644 xmake/plugins/xfetch/xmake.lua (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/tests/plugins/repository/test.lua b/tests/plugins/repository/test.lua new file mode 100644 index 000000000..0f7b1cc60 --- /dev/null +++ b/tests/plugins/repository/test.lua @@ -0,0 +1,56 @@ +function main() + local prog = os.programfile() + local gd = os.tmpfile() .. ".gd" + io.writefile(path.join(gd, ".xmake", "repositories", "xmake-repo", "plugins", "hello-world", "xmake.lua"), [[ +task("hello-world") + set_category("plugin") + on_run("main") + set_menu {usage = "xmake hello-world"} +]]) + io.writefile(path.join(gd, ".xmake", "repositories", "xmake-repo", "plugins", "hello-world", "main.lua"), [[ +function main() print("repo-ok") end +]]) + local out = os.iorunv(prog, {"hello-world"}, {envs = {XMAKE_GLOBALDIR = gd}}) + assert(out:find("repo%-ok", 1, true)) + local out = os.iorunv(prog, {"hello-world"}, {envs = {XMAKE_PROGRAM_DIR = os.programdir(), XMAKE_GLOBALDIR = gd}}) + assert(out:find("xrepo", 1, true)) + + local md = os.tmpfile() .. ".md" + io.writefile(path.join(md, ".xmake", "plugins", "manual-plugin", "xmake.lua"), [[ +task("manual-plugin") + set_category("plugin") + on_run("main") + set_menu {usage = "xmake manual-plugin"} +]]) + io.writefile(path.join(md, ".xmake", "plugins", "manual-plugin", "main.lua"), [[ +function main() print("manual") end +]]) + out = os.iorunv(prog, {"plugin", "--list"}, {envs = {XMAKE_GLOBALDIR = md}}) + assert(out:find("manual%-plugin", 1, true)) + out = os.iorunv(prog, {"plugin", "--list"}, {envs = {XMAKE_GLOBALDIR = gd}}) + assert(out:find("hello%-world", 1, true)) + + out = os.iorunv(prog, {"plugin", "--remove", "manual-plugin"}, {envs = {XMAKE_GLOBALDIR = md}}) + assert(out:find("remove plugin", 1, true)) + out = os.iorunv(prog, {"plugin", "--list"}, {envs = {XMAKE_GLOBALDIR = md}}) + assert(not out:find("manual%-plugin", 1, true)) + + local ld = os.tmpfile() .. ".ld" + io.writefile(path.join(ld, "plugins", "hello-world", "xmake.lua"), [[ +task("hello-world") + set_category("plugin") + on_run("main") + set_menu {usage = "xmake hello-world"} +]]) + io.writefile(path.join(ld, "plugins", "hello-world", "main.lua"), [[ +function main() print("local-ok") end +]]) + out = os.iorunv(prog, {"hello-world"}, {envs = {XMAKE_GLOBALDIR = gd, XMAKE_MAIN_REPO = ld}}) + assert(out:find("local%-ok", 1, true)) + out = os.iorunv(prog, {"hello-world"}, {envs = {XMAKE_GLOBALDIR = gd}}) + assert(out:find("repo%-ok", 1, true)) + + os.tryrm(gd) + os.tryrm(md) + os.tryrm(ld) +end diff --git a/xmake/core/base/task.lua b/xmake/core/base/task.lua index a51b1dbb5..15258f8cc 100644 --- a/xmake/core/base/task.lua +++ b/xmake/core/base/task.lua @@ -81,9 +81,24 @@ end -- the directories of tasks function task._directories() - return {path.join(global.directory(), "plugins"), - path.join(os.programdir(), "plugins"), - path.join(os.programdir(), "actions")} + local dirs = {path.join(global.directory(), "plugins")} + -- plugins from repositories cloned by `xrepo update-repo` + local reposdir = path.join(global.directory(), "repositories") + for _, dir in ipairs(os.dirs(path.join(reposdir, "*")) or {}) do + local plugindir = path.join(dir, "plugins") + if os.isdir(plugindir) then + table.insert(dirs, plugindir) + end + end + -- local checkout override (XMAKE_MAIN_REPO=/path/to/xmake-repo). + -- placed after the scanned repos so it takes precedence (table.join2 overwrites). + local repodir = os.getenv("XMAKE_MAIN_REPO") + if repodir and os.isdir(repodir) then + table.insert(dirs, path.join(repodir, "plugins")) + end + table.insert(dirs, path.join(os.programdir(), "plugins")) + table.insert(dirs, path.join(os.programdir(), "actions")) + return dirs end -- translate menu diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index 46858b51b..7a919f9aa 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -585,7 +585,6 @@ end -- - toolchain (is also binary) -- - library(default) -- - template --- - plugin -- function _instance:kind() local kind @@ -625,13 +624,6 @@ function _instance:is_template() return self:kind() == "template" end --- is plugin package? --- --- @return true if the package kind is "plugin" --- -function _instance:is_plugin() - return self:kind() == "plugin" -end -- is header-only library? -- @@ -760,9 +752,7 @@ function _instance:is_host() if requireinfo and requireinfo.host then return true end - -- we only get the kind once, because this function will be called frequently. e.g. in plat()/arch() - local kind = self:kind() - return kind == "binary" or kind == "toolchain" or kind == "plugin" + return self:is_binary() end -- is cross-compilation? @@ -1715,10 +1705,6 @@ function _instance:buildhash() if label then str = str .. label end - -- we need to distinguish the install directories of the plugin and package with the same name - if self:is_plugin() then - str = str .. "plugin" - end if configs then -- with old vs_runtime configs @@ -2127,16 +2113,6 @@ function _instance:fetch(opt) is_system = true end end - elseif self:is_plugin() then - - -- we can only fetch the plugin package from the xmake repository - if system ~= true and not self:is_thirdparty() then - local manifest = self:manifest_load() - if manifest then - fetchinfo = {version = manifest.version or self:version_str()} - is_system = false - end - end else -- only fetch it from the xmake repository first diff --git a/xmake/modules/package/manager/xmake/search_package.lua b/xmake/modules/package/manager/xmake/search_package.lua index 04d69d135..32991ae3e 100644 --- a/xmake/modules/package/manager/xmake/search_package.lua +++ b/xmake/modules/package/manager/xmake/search_package.lua @@ -27,43 +27,31 @@ function _search_package(packages, name, opt) local packagename = packageinfo.name local packagedata = packageinfo.data - -- 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 + 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 - else - version = versions[1] end + else + version = versions[1] 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 + local description = packagedata.description + if description 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, kind = packagedata.kind} - end + if not opt.require_version or version then + packages[packagename] = {name = packagename, version = version, description = description, reponame = packagedata.reponame} 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 95f424a0f..82df01a84 100644 --- a/xmake/modules/private/action/require/impl/actions/install.lua +++ b/xmake/modules/private/action/require/impl/actions/install.lua @@ -32,7 +32,6 @@ 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) @@ -514,11 +513,6 @@ 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 @@ -535,10 +529,6 @@ function main(package) test(package) end - -- the plugin has been registered and tested, we can discard the previous backup now - if package:is_plugin() then - plugins.confirm(package:name()) - end -- leave the package environments os.setenvs(oldenvs) @@ -569,10 +559,6 @@ function main(package) -- leave the package environments os.setenvs(oldenvs) - -- unregister the broken plugin package and restore the previous version - if package:is_plugin() then - plugins.rollback(package:name()) - end -- copy the invalid package directory to cache local installdir = package:installdir() diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua index a3b58a0bd..3ad758c3a 100644 --- a/xmake/modules/private/action/require/impl/package.lua +++ b/xmake/modules/private/action/require/impl/package.lua @@ -949,7 +949,6 @@ 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 dfe50c3a4..a10fc803d 100644 --- a/xmake/modules/private/action/require/impl/remove_packages.lua +++ b/xmake/modules/private/action/require/impl/remove_packages.lua @@ -23,7 +23,6 @@ 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) @@ -95,12 +94,7 @@ 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 4a546e091..bfec451a8 100644 --- a/xmake/modules/private/action/require/impl/repository.lua +++ b/xmake/modules/private/action/require/impl/repository.lua @@ -27,47 +27,9 @@ 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 dirs = {path.join("packages", packagename:sub(1, 1), packagename)} - -- we only find the plugin directories if this repository has plugins, - -- it can avoid unnecessary filesystem access, because most repositories only have packages - local has_plugins = _g._HAS_PLUGINS - if has_plugins == nil then - has_plugins = {} - _g._HAS_PLUGINS = has_plugins - end - if has_plugins[repodir] == nil then - has_plugins[repodir] = os.isdir(path.join(repodir, "plugins")) - end - if has_plugins[repodir] then - local plugindirs = {path.join("plugins", packagename), - path.join("plugins", packagename:sub(1, 1), packagename)} - if opt.kind == "plugin" then - -- find it from the plugin directories first - dirs = table.join(plugindirs, dirs) - else - table.join2(dirs, plugindirs) - end - 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, opt) +function _get_packagedir_from_locked_repo(packagename, locked_repo) -- find global repository directory local repo_global @@ -140,8 +102,8 @@ function _get_packagedir_from_locked_repo(packagename, locked_repo, opt) -- find package directory local foundir if ok then - local dir = _find_packagedir(repodir_local, packagename, opt) - if dir 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 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) @@ -202,11 +164,6 @@ function packagedir(packagename, opt) -- get cache key local reponame = opt.name local cachekey = packagename - if opt.kind then - -- use a separator that cannot appear in package names to avoid key collision, - -- e.g. package("helloplugin") and plugin("hello") - cachekey = cachekey .. "\0" .. opt.kind - end local locked_repo = opt.locked_repo if locked_repo then cachekey = cachekey .. locked_repo.url .. (locked_repo.commit or "") .. (locked_repo.branch or "") @@ -223,14 +180,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, opt) + foundir = _get_packagedir_from_locked_repo(packagename, locked_repo) end -- find the package directory from repositories if not foundir then for _, repo in ipairs(repositories()) do - local dir = _find_packagedir(repo:directory(), packagename, opt) - if dir and (not reponame or reponame == repo:name()) then + 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 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 f5434fb17..685b5c973 100644 --- a/xmake/modules/private/action/require/impl/uninstall_packages.lua +++ b/xmake/modules/private/action/require/impl/uninstall_packages.lua @@ -21,7 +21,6 @@ -- imports import("core.cache.localcache") import("private.action.require.impl.package") -import("private.action.require.impl.utils.plugins") -- uninstall packages -- uninstall required packages @@ -47,10 +46,6 @@ 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 deleted file mode 100644 index 1bcbe8bf5..000000000 --- a/xmake/modules/private/action/require/impl/utils/plugins.lua +++ /dev/null @@ -1,81 +0,0 @@ ---!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()) - -- we copy it to the temporary directory first and then swap it, - -- so the previous plugin will not be lost if copying fails. e.g. disk full - -- - -- we cannot use the sibling directory, e.g. `plugins/.tmp`, - -- because the task loader will load all plugins from `plugins/*/xmake.lua` - local tmpdir = path.join(path.directory(dir), ".tmp", package:name()) - os.tryrm(tmpdir) - os.cp(installdir, tmpdir) - -- 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(tmpdir, "logs")) - -- replace the previous plugin only after the new one is fully ready, - -- and we backup the previous plugin, it can be restored by `rollback()` if the installation fails later. e.g. test failure - local bakdir = tmpdir .. ".bak" - os.tryrm(bakdir) - if os.isdir(dir) then - os.mv(dir, bakdir) - end - os.mv(tmpdir, dir) - vprint("register plugin(%s) to %s", package:name(), dir) -end - --- confirm the registered plugin and discard the previous backup -function confirm(name) - os.tryrm(path.join(path.directory(plugindir(name)), ".tmp", name .. ".bak")) -end - --- rollback the registered plugin and restore the previous backup if the installation fails -function rollback(name) - local dir = plugindir(name) - local bakdir = path.join(path.directory(dir), ".tmp", name .. ".bak") - if os.isdir(bakdir) then - os.tryrm(dir) - os.mv(bakdir, dir) - vprint("restore the previous plugin(%s) to %s", name, dir) - end -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 73a32b836..d839b92cb 100644 --- a/xmake/modules/private/action/require/search.lua +++ b/xmake/modules/private/action/require/search.lua @@ -20,7 +20,6 @@ -- 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") @@ -42,18 +41,6 @@ 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 errors then - raise(errors) - end - if type(extrainfo) == "table" then - opt.kind = extrainfo.kind - end - end -- show title print("The package names:") @@ -71,10 +58,8 @@ function main(names) local version = result.version local reponame = result.reponame local description = result.description - local kind = result.kind - cprint(" -> ${color.dump.string}%s%s${clear}%s: %s %s", name, + cprint(" -> ${color.dump.string}%s%s${clear}: %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 d2c7afd10..4a9c309e3 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" or value == "plugin" + return value == "binary" or value == "toolchain" or value == "template" end})) end diff --git a/xmake/modules/private/xrepo/action/install.lua b/xmake/modules/private/xrepo/action/install.lua index d235b7a98..b0d297b38 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 or install plugin package.", - values = {"static", "shared", "plugin"}}, + {'k', "kind", "kv", nil, "Enable static/shared library.", + values = {"static", "shared"} }, {'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,10 +47,6 @@ 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" }, @@ -97,7 +93,6 @@ 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} } @@ -183,7 +178,7 @@ function _install_packages(packages) table.insert(config_argv, mode) end local kind = option.get("kind") - if kind and kind ~= "plugin" then + if kind then table.insert(config_argv, "-k") table.insert(config_argv, kind) end @@ -251,15 +246,7 @@ function _install_packages(packages) if #rcfiles > 0 then envs.XMAKE_RCFILES = path.joinenv(rcfiles) end - -- we can skip the repeated configuration and toolchain detection to speed up the plugin installation, - -- because the plugin package is host-only and does not depend on any build configuration. - -- we only skip it if no extra configuration arguments are given, e.g. `xrepo install -k plugin hello` - if kind == "plugin" and #config_argv == 3 - and os.isfile(path.join(workdir, ".xmake", os.host(), os.arch(), "xmake.conf")) then - vprint("skip the configuration for installing plugins") - else - os.vrunv(os.programfile(), config_argv, {envs = envs}) - end + os.vrunv(os.programfile(), config_argv, {envs = envs}) -- do install local require_argv = {"require"} @@ -298,9 +285,7 @@ function _install_packages(packages) if mode == "debug" then extra.debug = true end - if kind == "plugin" then - extra.kind = "plugin" - elseif kind then + if kind then extra.configs = extra.configs or {} extra.configs.shared = kind == "shared" end @@ -321,18 +306,6 @@ 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 989983955..ed92a83b8 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 or remove plugin package.", - values = {"static", "shared", "plugin"} }, + {'k', "kind", "kv", nil, "Enable static/shared library.", + values = {"static", "shared"} }, {'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,8 +56,7 @@ 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 -k plugin hello-world" } + " - xrepo remove conan::zlib/1.2.11 vcpkg::zlib" } } -- show menu options @@ -138,7 +137,7 @@ function _remove_packages(packages) table.insert(config_argv, mode) end local kind = option.get("kind") - if kind and kind ~= "plugin" then + if kind then table.insert(config_argv, "-k") table.insert(config_argv, kind) end @@ -152,15 +151,7 @@ function _remove_packages(packages) if #rcfiles > 0 then envs.XMAKE_RCFILES = path.joinenv(rcfiles) end - -- we can skip the repeated configuration and toolchain detection to speed up the plugin removal, - -- because the plugin package is host-only and does not depend on any build configuration. - -- we only skip it if no extra configuration arguments are given, e.g. `xrepo remove -k plugin hello` - if kind == "plugin" and #config_argv == 3 - and os.isfile(path.join(workdir, ".xmake", os.host(), os.arch(), "xmake.conf")) then - vprint("skip the configuration for removing plugins") - else - os.vrunv(os.programfile(), config_argv, {envs = envs}) - end + os.vrunv(os.programfile(), config_argv, {envs = envs}) -- do remove local require_argv = {"require", "--uninstall"} @@ -177,9 +168,7 @@ function _remove_packages(packages) if mode == "debug" then extra.debug = true end - if kind == "plugin" then - extra.kind = "plugin" - elseif kind then + if 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 ff379c5db..d389e3833 100644 --- a/xmake/modules/private/xrepo/action/search.lua +++ b/xmake/modules/private/xrepo/action/search.lua @@ -30,14 +30,10 @@ 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 -k plugin", - " - xrepo search -k plugin hello-world"} + " - xrepo search \"pcre*\""} } -- show menu options @@ -84,11 +80,6 @@ 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 @@ -96,10 +87,6 @@ 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 8edfce584..cf75a212d 100644 --- a/xmake/modules/private/xrepo/quick_search/cache.lua +++ b/xmake/modules/private/xrepo/quick_search/cache.lua @@ -36,23 +36,9 @@ function _list_package_dirs() local subdirname = path.basename(path.directory(dir)) if #subdirname == 1 then -- ignore l/luajit/port/xmake.lua local packagename = path.filename(dir) - if not unique["package\0" .. packagename] then + if not unique[packagename] then table.insert(packageinfos, {name = packagename, repo = repo, packagedir = dir}) - unique["package\0" .. packagename] = true - end - end - end - -- find the plugin package directories, e.g. plugins/hello/xmake.lua, plugins/h/hello/xmake.lua - -- the plugin and package can share the same name, so we should not dedupe them with each other - 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["plugin\0" .. packagename] then - table.insert(packageinfos, {name = packagename, repo = repo, packagedir = dir}) - unique["plugin\0" .. packagename] = true + unique[packagename] = true end end end @@ -71,12 +57,10 @@ end function update() for _, packageinfo in ipairs(_list_package_dirs()) do local package = core_package.load_from_repository(packageinfo.name, packageinfo.packagedir, {repo = packageinfo.repo}) - -- we add the kind to the cache key to avoid the key collision between the plugin and package with the same name - cache:set(packageinfo.name .. "\0" .. (package:kind() or "library"), { + cache:set(packageinfo.name, { reponame = package:repo() and package:repo():name(), description = package:description(), versions = package:versions(), - kind = package:kind(), }) end cache:save() @@ -99,10 +83,7 @@ function find(name, opt) _init() opt = opt or {} local list_result = {} - for key, packagedata in pairs(cache:data()) do - -- strip the kind from the cache key, e.g. `zlib\0library` - -- and it is also compatible with the old cache data without the kind - local packagename = key:split("\0", {plain = true})[1] + for packagename, packagedata in pairs(cache:data()) do local found = false if opt.prefix then found = packagename:startswith(name) diff --git a/xmake/modules/private/xrepo/quick_search/completion.lua b/xmake/modules/private/xrepo/quick_search/completion.lua index a78ec2817..449a988ea 100644 --- a/xmake/modules/private/xrepo/quick_search/completion.lua +++ b/xmake/modules/private/xrepo/quick_search/completion.lua @@ -22,15 +22,10 @@ import("private.xrepo.quick_search.cache") -- complete xrepo packages function _xmake_package_complete(complete, opt) - local unique = {} local candidates = {} local found = cache.find(complete, {prefix = true}) for _, candidate in ipairs(found) do - -- the plugin and package can share the same name, we need to remove the duplicates - if not unique[candidate.name] then - table.insert(candidates, {value = candidate.name, description = candidate.data.description}) - unique[candidate.name] = true - end + table.insert(candidates, {value = candidate.name, description = candidate.data.description}) end return candidates end diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index e12a433e2..276508396 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -62,42 +62,19 @@ end function _save_manifest(manifest) io.save(_manifest_path(), manifest) end - --- is the plugin package name? e.g. hello-world, myrepo@hello-world --- --- the plugin url is the git url or local path, e.g. --- https://github.com/xmake-io/xmake-plugins, git@github.com:xmake-io/xmake-plugins.git, /tmp/xmake-plugins -function _is_package_name(str) - return not os.isdir(str) and not str:find("[/\\:]") +-- install the plugin by name from the repository. +-- Repository plugins are loaded directly from the checkout; +-- ensure the repository is up to date with `xrepo update-repo`. +function _install_name(name) + print("plugin %s will be loaded from the repository after xrepo update-repo.", name) end - --- install the plugin package from repositories, e.g. xmake plugin --install hello-world -function _install_package(name, opt) - opt = opt or {} - local argv = {"lua", "private.xrepo", "install", "--kind=plugin"} - if opt.force then - table.insert(argv, "--force") - end - if option.get("yes") then - table.insert(argv, "-y") - end - if option.get("verbose") then - table.insert(argv, "-v") - end - if option.get("diagnosis") then - table.insert(argv, "-D") - end - table.insert(argv, name) - os.execv(os.programfile(), argv) -end - -- install plugins function _install() - -- install the plugin package from repositories? + -- install the plugin by name from the repository? local name = option.get("plugins") - if name and _is_package_name(name) then - return _install_package(name) + if name and not os.isdir(name) and not name:find("[/\\:]") then + return _install_name(name) end -- enter environment @@ -151,13 +128,6 @@ end -- update plugins function _update() - - -- update the plugin package from repositories? e.g. xmake plugin --update hello-world - local name = option.get("plugins") - if name and _is_package_name(name) then - return _install_package(name, {force = true}) - end - -- enter environment environment.enter() @@ -198,11 +168,9 @@ function _update() environment.leave() end --- remove the given installed plugin +-- remove the given installed plugin (from ~/.xmake/plugins/) function _remove() local name = assert(option.get("plugins"), "please specify the plugin name to be removed!") - -- avoid escaping the plugins directory, e.g. `xmake plugin --remove ../foo`, - -- and `.` or the empty name will be resolved to the plugins directory itself assert(name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name) local plugindir = path.join(global.directory(), "plugins", name) assert(os.isdir(plugindir), "plugin(%s) not found!", name) @@ -210,24 +178,63 @@ function _remove() cprint("${color.success}remove plugin(%s) ok!", name) end --- list all installed plugins +-- list all installed plugins (manual + repository) function _list() + local seen = {} + -- manually installed plugins local plugindir = path.join(global.directory(), "plugins") cprint("plugins in ${bright}%s${clear}:", plugindir) - for _, dir in ipairs(os.dirs(path.join(plugindir, "*"))) do + local found = false + for _, dir in ipairs(os.dirs(path.join(plugindir, "*")) or {}) do if os.isfile(path.join(dir, "xmake.lua")) then - local version, description - local manifest_file = path.join(dir, "manifest.txt") - if os.isfile(manifest_file) then - local manifest = io.load(manifest_file) - if manifest then - version = manifest.version - description = manifest.description + local name = path.filename(dir) + seen[name] = true + found = true + cprint(" ${color.dump.string}%s${clear}", name) + end + end + if not found then + print(" (none)") + end + + -- repository plugins (from scanned repos) + local reposdir = path.join(global.directory(), "repositories") + for _, dir in ipairs(os.dirs(path.join(reposdir, "*")) or {}) do + local rplugindir = path.join(dir, "plugins") + if os.isdir(rplugindir) then + local reponame = path.filename(dir) + cprint("plugins in repository ${bright}%s${clear}:", reponame) + local repofound = false + for _, subdir in ipairs(os.dirs(path.join(rplugindir, "*")) or {}) do + if os.isfile(path.join(subdir, "xmake.lua")) and not seen[path.filename(subdir)] then + seen[path.filename(subdir)] = true + repofound = true + cprint(" ${color.dump.string}%s${clear}", path.filename(subdir)) + end + end + if not repofound then + print(" (none)") + end + end + end + + -- local checkout plugins + local repodir = os.getenv("XMAKE_MAIN_REPO") + if repodir and os.isdir(repodir) then + local rplugindir = path.join(repodir, "plugins") + if os.isdir(rplugindir) then + cprint("plugins in ${bright}XMAKE_MAIN_REPO${clear}:") + local repofound = false + for _, subdir in ipairs(os.dirs(path.join(rplugindir, "*")) or {}) do + if os.isfile(path.join(subdir, "xmake.lua")) and not seen[path.filename(subdir)] then + seen[path.filename(subdir)] = true + repofound = true + cprint(" ${color.dump.string}%s${clear}", path.filename(subdir)) end end - cprint(" ${color.dump.string}%s${clear}%s: %s", path.filename(dir), - version and ("-" .. version) or "", - description or "") + if not repofound then + print(" (none)") + end end end end @@ -254,4 +261,3 @@ function main() _clear() end end - diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index b61dd05f9..87d85e81d 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -36,7 +36,6 @@ task("plugin") " $ xmake plugin --install hello-world", " $ xmake plugin --remove hello-world", " $ xmake plugin --list", - " $ xmake plugin --update", - " $ xmake plugin --update hello-world"} + " $ xmake plugin --update"} } } diff --git a/xmake/plugins/xfetch/main.lua b/xmake/plugins/xfetch/main.lua deleted file mode 100644 index 357219486..000000000 --- a/xmake/plugins/xfetch/main.lua +++ /dev/null @@ -1,289 +0,0 @@ ---!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 main.lua --- - --- imports -import("core.base.option") -import("core.base.global") -import("core.package.package", {alias = "core_package"}) - --- the small ascii logos for the known systems, like fastfetch -local logos = { - xmake = {color = "green", lines = { - [[ _ ]], - [[__ ___ __ __ __ _| | ______]], - [[\ \/ / | \/ |/ _ | |/ / __ \]], - [[ > < | \__/ | /_| | < ___/]], - [[/_/\_\_|_| |_|\__ \|_|\_\____|]]}}, - linux = {color = "yellow", lines = { - [[ .--.]], - [[ |o_o |]], - [[ |:_/ |]], - [[ // \ \]], - [[ (| | )]], - [[/'\_ _/`\]], - [[\___)=(___/]]}}, - windows = {color = "blue", lines = { - [[ ______ ______]], - [[| | |]], - [[|______|______|]], - [[| | |]], - [[|______|______|]]}}, - macosx = {color = "white", lines = { - [[ .:']], - [[ _ :'_]], - [[ .'`_`-'_``.]], - [[:________.-']], - [[:_______:]], - [[ :_______`-;]], - [[ `._.-._.']]}}, - bsd = {color = "red", lines = { - [[/\,-'''''-,/\]], - [[\_) (_/]], - [[| |]], - [[| |]], - [[ ; ;]], - [[ '-_____-']]}}, - archlinux = {color = "cyan", lines = { - [[ /\]], - [[ / \]], - [[ /\ \]], - [[ / \]], - [[ / ,, \]], - [[ / | | -\]], - [[/_-'' ''-_\]]}}, - ubuntu = {color = "red", lines = { - [[ _]], - [[ ---(_)]], - [[ _/ --- \]], - [[(_) | |]], - [[ \ --- _/]], - [[ ---(_)]]}}, - debian = {color = "red", lines = { - [[ _____]], - [[ / __ \]], - [[| / |]], - [[| \___-]], - [[-_]], - [[ --_]]}}, - fedora = {color = "blue", lines = { - [[ _____]], - [[ / __)\]], - [[ | / \ \]], - [[ ___| |__/ /]], - [[ / (_ _)_/]], - [[/ / | |]], - [[\ \__/ |]], - [[ \(_____/]]}}, - centos = {color = "yellow", lines = { - [[ ____^____]], - [[ |\ | /|]], - [[ | \ | / |]], - [[<---- ---->]], - [[ | / | \ |]], - [[ |/__|__\|]], - [[ v]]}}, - linuxmint = {color = "green", lines = { - [[ ___________]], - [[|_ \]], - [[ | | _____ |]], - [[ | | | | | |]], - [[ | | | | | |]], - [[ | \_____/ |]], - [[ \_________/]]}}, - gentoo = {color = "magenta", lines = { - [[ _-----_]], - [[( \]], - [[\ 0 \]], - [[ \ )]], - [[ / _/]], - [[( _-]], - [[\____-]]}}, - opensuse = {color = "green", lines = { - [[ _______]], - [[__| __ \]], - [[ / .\ \]], - [[ \__/ |]], - [[ _______|]], - [[ \_______]], - [[__________/]]}}, - manjaro = {color = "green", lines = { - [[||||||||| ||||]], - [[||||||||| ||||]], - [[|||| ||||]], - [[|||| |||| ||||]], - [[|||| |||| ||||]], - [[|||| |||| ||||]], - [[|||| |||| ||||]]}}, - nixos = {color = "blue", lines = { - [[ \\ \\ //]], - [[ ==\\__\\/ //]], - [[ // \\//]], - [[==// //==]], - [[ //\\___//]], - [[// /\\ \\==]], - [[ // \\ \\]]}}, - alpine = {color = "blue", lines = { - [[ /\ /\]], - [[ / \ \]], - [[ / \ \]], - [[/ \ \]], - [[ \ \]], - [[ \]]}} -} --- get the logo of the current system -function _get_logo() - local name = option.get("logo") - if not name then - -- os.host() is always the real host system, e.g. windows, linux, macosx .. - -- even if we are running in the msys/cygwin subsystem, @see os.subhost() - name = os.host() - if name == "linux" then - name = linuxos.name() - end - end - -- we will show the linux or xmake logo if the logo drawing is not found - local logo = logos[name] - if not logo then - logo = os.host() == "linux" and logos.linux or logos.xmake - end - return logo -end - --- get the user and host name -function _get_title() - local user = os.getenv("USER") or os.getenv("USERNAME") or "user" - local host = os.getenv("HOSTNAME") or os.getenv("COMPUTERNAME") - if not host and is_host("linux", "macosx", "bsd") and os.isfile("/etc/hostname") then - local content = io.readfile("/etc/hostname") - if content then - host = content:trim() - end - end - return user .. "@" .. (host or os.host()) -end - --- get the operation system name and version -function _get_os() - if is_host("linux") then - -- the system version may be unavailable on the rolling release distributions - local version = try { function () return linuxos.version() end } - return linuxos.name() .. (version and (" " .. tostring(version)) or "") .. " " .. os.arch() - elseif is_host("macosx") then - local version = try { function () return macos.version() end } - return "macOS" .. (version and (" " .. tostring(version)) or "") .. " " .. os.arch() - elseif is_host("windows") then - local version = try { function () return winos.version() end } - return "Windows" .. (version and (" " .. tostring(version)) or "") .. " " .. os.arch() - end - return os.host() .. " " .. os.arch() -end - --- get the cpu name -function _get_cpu() - local name = os.cpuinfo("model_name") or os.cpuinfo("vendor") or "unknown" - return string.format("%s (%d)", name, os.cpuinfo("ncpu") or 1) -end - --- format the given size in MB as GiB, we do not use `%.1f` to avoid the locale decimal separator -function _format_gib(size_mb) - local gib10 = math.floor(size_mb / 1024 * 10 + 0.5) - return string.format("%d.%d GiB", math.floor(gib10 / 10), gib10 % 10) -end - --- get the memory usage, the sizes of os.meminfo() are in MB -function _get_memory() - local meminfo = os.meminfo() - if meminfo.totalsize and meminfo.availsize then - -- `%%%%` will be shown as `%`, because cprint will format this string again - return string.format("%s / %s (%d%%%%)", - _format_gib(meminfo.totalsize - meminfo.availsize), - _format_gib(meminfo.totalsize), - math.floor(meminfo.usagerate * 100)) - end -end - --- get the count of the installed packages -function _get_packages() - local count = #os.dirs(path.join(core_package.installdir(), "*", "*")) - return string.format("%d (xrepo)", count) -end - --- get the count of the installed plugins -function _get_plugins() - return tostring(#os.files(path.join(global.directory(), "plugins", "*", "xmake.lua"))) -end - --- get all the information lines -function _get_infolines(color) - local title = _get_title() - local infos = { - {"OS", _get_os()}, - {"CPU", _get_cpu()}, - {"Memory", _get_memory()}, - {"Shell", os.shell()}, - {"Terminal", os.term()}, - {"xmake", "v" .. xmake.version()}, - {"Packages", _get_packages()}, - {"Plugins", _get_plugins()}, - {"Theme", global.get("theme") or "default"} - } - if is_host("linux") then - local kernelver = try { function () return linuxos.kernelver() end } - if kernelver then - table.insert(infos, 2, {"Kernel", tostring(kernelver)}) - end - end - local lines = {} - table.insert(lines, "${bright " .. color .. "}" .. title .. "${clear}") - table.insert(lines, string.rep("-", #title)) - for _, info in ipairs(infos) do - if info[2] then - table.insert(lines, "${bright " .. color .. "}" .. info[1] .. "${clear}: " .. info[2]) - end - end - table.insert(lines, "") - table.insert(lines, "${onblack} ${onred} ${ongreen} ${onyellow} ${onblue} ${onmagenta} ${oncyan} ${onwhite} ${clear}") - return lines -end - -function main() - local logo = _get_logo() - local logolines = logo.lines - local infolines = _get_infolines(logo.color) - -- show the logo and information side by side, like fastfetch - local width = 0 - for _, line in ipairs(logolines) do - width = math.max(width, #line) - end - local startline = 1 - if #infolines > #logolines then - startline = math.floor((#infolines - #logolines) / 2) + 1 - end - print("") - for i = 1, math.max(#logolines, #infolines) do - local left = logolines[i - startline + 1] or "" - local right = infolines[i] or "" - left = left .. string.rep(" ", width - #left) - -- escape `%`, because cprint will format this string again - left = left:gsub("%%", "%%%%") - cprint(" ${bright %s}%s${clear} %s", logo.color, left, right) - end - print("") -end diff --git a/xmake/plugins/xfetch/xmake.lua b/xmake/plugins/xfetch/xmake.lua deleted file mode 100644 index a3296d233..000000000 --- a/xmake/plugins/xfetch/xmake.lua +++ /dev/null @@ -1,32 +0,0 @@ ---!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 xmake.lua --- - -task("xfetch") - set_category("plugin") - on_run("main") - set_menu { - usage = "xmake xfetch [options]", - description = "Fetch and show the system and xmake information.", - options = { - {'l', "logo", "kv", nil, "Show the given logo.", - "e.g.", - " $ xmake xfetch --logo=archlinux"} - } - } -- cgit v1.3.1 From 0376ff7ab486d773458886982f465897ca18a978 Mon Sep 17 00:00:00 2001 From: Saikari Date: Mon, 27 Jul 2026 11:51:12 +0300 Subject: Enhance plugin management by adding support for installing plugins from GitHub and local directories, and streamline repository handling in the task system. --- tests/plugins/repository/test.lua | 136 ++++++++++++---------- xmake/core/base/task.lua | 21 +--- xmake/plugins/plugin/main.lua | 229 ++++++++++++++++++++++++++------------ xmake/plugins/plugin/xmake.lua | 4 + 4 files changed, 245 insertions(+), 145 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/tests/plugins/repository/test.lua b/tests/plugins/repository/test.lua index 07d11eb37..bbff847a5 100644 --- a/tests/plugins/repository/test.lua +++ b/tests/plugins/repository/test.lua @@ -1,70 +1,90 @@ -function main() - -- backup existing env vars - local prev_globaldir = os.getenv("XMAKE_GLOBALDIR") - local prev_main_repo = os.getenv("XMAKE_MAIN_REPO") - - local gd = os.tmpfile() .. ".gd" - io.writefile(gd .. "/.xmake/repositories/xmake-repo/plugins/hello-world/xmake.lua", [[ -task("hello-world") +function _write_plugin(dir, name, text) + io.writefile(path.join(dir, "xmake.lua"), string.format([[ +task("%s") set_category("plugin") on_run("main") - set_menu {usage = "xmake hello-world"} -]]) - io.writefile(gd .. "/.xmake/repositories/xmake-repo/plugins/hello-world/main.lua", [[ -function main() print("repo-ok") end -]]) + set_menu {usage = "xmake %s"} +]], name, name)) + io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], text)) +end - os.setenv("XMAKE_GLOBALDIR", gd) - os.exec("xmake hello-world") +function main() + local global = import("core.base.global") + local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") + local reponame = "plugin-test-repository-" .. suffix + local hello_name = "plugin-test-hello-" .. suffix + local formatter_name = "plugin-test-formatter-" .. suffix + local available_name = "plugin-test-available-" .. suffix + local local_name = "plugin-test-local-" .. suffix + local repodir = os.tmpfile() .. ".plugins-repository" + local localdir = path.join(os.tmpfile() .. ".local-plugin", local_name) + local plugindir = path.join(global.directory(), "plugins") + local cachefile = path.join(global.directory(), "cache", "repository") + local cachebackup = os.tmpfile() .. ".repository" - os.exec("xmake plugin --install hello-world") + if os.isfile(cachefile) then + os.cp(cachefile, cachebackup) + end - local md = os.tmpfile() .. ".md" - io.writefile(md .. "/.xmake/plugins/manual-plugin/xmake.lua", [[ -task("manual-plugin") - set_category("plugin") - on_run("main") - set_menu {usage = "xmake manual-plugin"} -]]) - io.writefile(md .. "/.xmake/plugins/manual-plugin/main.lua", [[ -function main() print("manual") end -]]) + try + { + function () + -- mock repository with installed and available plugins + _write_plugin(path.join(repodir, "plugins", hello_name), hello_name, "repo-ok") + _write_plugin(path.join(repodir, "plugins", formatter_name), formatter_name, "format-ok") + _write_plugin(path.join(repodir, "plugins", available_name), available_name, "available-ok") + local cache = io.load(cachefile) or {} + cache.repositories = cache.repositories or {} + cache.repositories[reponame] = {repodir} + io.save(cachefile, cache) - os.setenv("XMAKE_GLOBALDIR", md) - os.exec("xmake plugin --list") - os.exec("xmake plugin --remove manual-plugin") + -- Feature: install by plain name from repository + os.exec("xmake plugin --install " .. hello_name) + os.exec("xmake " .. hello_name) - local ld = os.tmpfile() .. ".ld" - io.writefile(ld .. "/plugins/hello-world/xmake.lua", [[ -task("hello-world") - set_category("plugin") - on_run("main") - set_menu {usage = "xmake hello-world"} -]]) - io.writefile(ld .. "/plugins/hello-world/main.lua", [[ -function main() print("local-ok") end -]]) + -- Feature: install by repo@name format + os.exec("xmake plugin --install " .. reponame .. "@" .. formatter_name) + os.exec("xmake " .. formatter_name) - os.setenv("XMAKE_GLOBALDIR", gd) - os.setenv("XMAKE_MAIN_REPO", ld) - os.exec("xmake hello-world") + -- Feature: --list shows installed and available repository plugins + local out = os.iorun("xmake plugin --list") + assert(out:find(hello_name, 1, true)) + assert(out:find(formatter_name, 1, true)) + assert(out:find(available_name, 1, true)) + assert(out:find("xmake plugin --install " .. available_name, 1, true)) - os.setenv("XMAKE_MAIN_REPO", "") - os.exec("xmake hello-world") + -- Feature: install from local directory + _write_plugin(localdir, local_name, "local-ok") + os.exec("xmake plugin --install " .. os.args(localdir)) + os.exec("xmake " .. local_name) - -- restore env vars - if prev_globaldir then - os.setenv("XMAKE_GLOBALDIR", prev_globaldir) - else - os.setenv("XMAKE_GLOBALDIR", "") - end - if prev_main_repo then - os.setenv("XMAKE_MAIN_REPO", prev_main_repo) - else - os.setenv("XMAKE_MAIN_REPO", "") - end + out = os.iorun("xmake plugin --list") + assert(out:find(local_name, 1, true)) + + -- Feature: remove plugin + os.exec("xmake plugin --remove " .. local_name) + out = os.iorun("xmake plugin --list") + assert(not out:find(local_name, 1, true)) - os.tryrm(gd) - os.tryrm(md) - os.tryrm(ld) + -- Feature: install non-existent plugin fails gracefully + local ok = try { function () os.exec("xmake plugin --install plugin-test-missing-" .. suffix) end } + assert(not ok) + end, + finally + { + function () + for _, name in ipairs({hello_name, formatter_name, available_name, local_name}) do + os.tryrm(path.join(plugindir, name)) + end + if os.isfile(cachebackup) then + os.cp(cachebackup, cachefile) + else + os.tryrm(cachefile) + end + os.tryrm(cachebackup) + os.tryrm(repodir) + os.tryrm(path.directory(localdir)) + end + } + } end diff --git a/xmake/core/base/task.lua b/xmake/core/base/task.lua index 15258f8cc..a51b1dbb5 100644 --- a/xmake/core/base/task.lua +++ b/xmake/core/base/task.lua @@ -81,24 +81,9 @@ end -- the directories of tasks function task._directories() - local dirs = {path.join(global.directory(), "plugins")} - -- plugins from repositories cloned by `xrepo update-repo` - local reposdir = path.join(global.directory(), "repositories") - for _, dir in ipairs(os.dirs(path.join(reposdir, "*")) or {}) do - local plugindir = path.join(dir, "plugins") - if os.isdir(plugindir) then - table.insert(dirs, plugindir) - end - end - -- local checkout override (XMAKE_MAIN_REPO=/path/to/xmake-repo). - -- placed after the scanned repos so it takes precedence (table.join2 overwrites). - local repodir = os.getenv("XMAKE_MAIN_REPO") - if repodir and os.isdir(repodir) then - table.insert(dirs, path.join(repodir, "plugins")) - end - table.insert(dirs, path.join(os.programdir(), "plugins")) - table.insert(dirs, path.join(os.programdir(), "actions")) - return dirs + return {path.join(global.directory(), "plugins"), + path.join(os.programdir(), "plugins"), + path.join(os.programdir(), "actions")} end -- translate menu diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index e1525d51c..21c4b5267 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -21,11 +21,23 @@ -- imports import("core.base.option") import("core.base.global") +import("core.package.repository") import("devel.git") import("net.fasturl") import("private.action.require.impl.environment") --- get plugin urls +-- get plugin directory in ~/.xmake/plugins +function _get_plugindir(name) + local plugindir = path.join(global.directory(), "plugins") + return name and path.join(plugindir, name) or plugindir +end + +-- get local and global repositories, with local taking precedence +function _repositories() + return table.join(repository.repositories({global = false}), repository.repositories({global = true})) +end + +-- get plugin urls for batch install function _plugin_urls() local urls = option.get("plugins") if urls then @@ -45,9 +57,8 @@ function _plugin_urls() return urls end --- get manifest path function _manifest_path() - return path.join(global.directory(), "plugins", "manifest.txt") + return path.join(_get_plugindir(), "manifest.txt") end -- load manifest @@ -62,20 +73,87 @@ end function _save_manifest(manifest) io.save(_manifest_path(), manifest) end --- install the plugin by name from the repository. --- Repository plugins are loaded directly from the checkout; --- ensure the repository is up to date with `xrepo update-repo`. -function _install_name(name) - print("plugin %s will be loaded from the repository after xrepo update-repo.", name) + +-- find a plugin directory in the given repository directory +function _find_plugin_in_repo(repodir, name) + local dir = path.join(repodir, "plugins", name) + if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then + return dir + end end --- install plugins -function _install() - -- install the plugin by name from the repository? - local name = option.get("plugins") - if name and not os.isdir(name) and not name:find("[/\\:]") then - return _install_name(name) +-- install a plugin from the given repository or the first repository containing it +function _install_plugins_from_repo(name, reponame) + for _, repo in ipairs(_repositories()) do + if not reponame or repo:name() == reponame then + local srcdir = _find_plugin_in_repo(repo:directory(), name) + if srcdir then + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint("${color.success}install ${bright}%s${clear} from repository ${bright}%s${clear} ok!", name, repo:name()) + return + end + end end + if reponame then + raise("plugin(%s): not found in repository %s!", name, reponame) + end + raise("plugin(%s): not found in any repository! try ${bright}xrepo update-repo${clear} first.", name) +end + +-- install a plugin from a local directory +function _install_from_local(dir) + assert(os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")), "plugin path(%s): ${bright}xmake.lua${clear} not found!", dir) + local name = path.filename(path.absolute(dir)) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(dir, dstdir) + cprint("${color.success}install ${bright}%s${clear} from ${bright}%s${clear} ok!", name, dir) +end + +-- install a plugin from a git url or github shortcut +function _install_from_git(url) + local branch + if url:startswith("github:") then + url = url:sub(8) + local i = url:find("#", 1, true) + if i then + branch = url:sub(i + 1) + url = url:sub(1, i - 1) + end + url = "https://github.com/" .. url .. ".git" + end + local tmpdir = os.tmpfile() .. ".dir" + local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir} + if branch then + clone_opt.branch = branch + end + git.clone(git.asgiturl(url) or url, clone_opt) + local found = false + local function install(srcdir, name) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + found = true + end + if os.isfile(path.join(tmpdir, "xmake.lua")) then + install(tmpdir, path.basename(path.filename(url))) + else + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + install(srcdir, path.filename(srcdir)) + end + end + os.tryrm(tmpdir) + if not found then + raise("no plugin found in %s", url) + end +end + +-- install plugins +function _install() -- enter environment environment.enter() @@ -85,12 +163,45 @@ function _install() function () -- do install + local name = option.get("plugins") + if name and #name > 0 then + -- parse repo@plugin format + local i = name:find("@", 1, true) + if i and not name:find("[/\\:]") then + local reponame = name:sub(1, i - 1) + local pluginname = name:sub(i + 1) + _install_plugins_from_repo(pluginname, reponame) + return + end + + -- github shortcut: github:user/repo or github:user/repo#branch + if name:startswith("github:") then + _install_from_git(name) + return + end + + -- git url or local path + if os.isdir(name) or name:find("[/\\:]") then + if os.isdir(name) then + _install_from_local(name) + else + _install_from_git(name) + end + return + end + + -- plain name: try to find it in repositories + _install_plugins_from_repo(name) + return + end + + -- do batch install from plugin collection urls local urls = _plugin_urls() local tmpdir = os.tmpfile() .. ".dir" - local plugindir = path.join(global.directory(), "plugins") + local plugindir = _get_plugindir() local installed_url for _, url in ipairs(urls) do - cprint("installing plugins from %s ..", url) + cprint("installing plugins from ${bright}%s${clear} ..", url) git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) installed_url = url break @@ -98,21 +209,20 @@ function _install() for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do local srcdir = path.directory(filepath) local name = path.filename(srcdir) - local dstdir = path.join(plugindir, name) + local dstdir = _get_plugindir(name) assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) os.vcp(srcdir, dstdir) - cprint(" ${yellow}->${clear} %s", name) + cprint(" ${color.success}-> ${bright}%s${clear}", name) end os.tryrm(tmpdir) - -- save manifest if installed_url then local manifest = _load_manifest() or {} manifest.urls = manifest.urls or {} table.join2(manifest.urls, installed_url) _save_manifest(manifest) end - cprint("${bright}all plugins have been installed in %s!", plugindir) + cprint("${color.success}all plugins have been installed in ${bright}%s${clear}!", plugindir) end, catch { @@ -140,22 +250,22 @@ function _update() local manifest = _load_manifest() assert(manifest and manifest.urls, "3rd plugins not found!") local urls = manifest.urls - local plugindir = path.join(global.directory(), "plugins") + local plugindir = _get_plugindir() for _, url in ipairs(urls) do - cprint("updating plugins from %s ..", url) + cprint("updating plugins from ${bright}%s${clear} ..", url) local tmpdir = os.tmpfile() .. ".dir" git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do local srcdir = path.directory(filepath) local name = path.filename(srcdir) - local dstdir = path.join(plugindir, name) + local dstdir = _get_plugindir(name) os.tryrm(dstdir) os.vcp(srcdir, dstdir) - cprint(" ${yellow}->${clear} %s", name) + cprint(" ${color.success}-> ${bright}%s${clear}", name) end os.tryrm(tmpdir) end - cprint("${bright}all plugins have been updated in %s!", plugindir) + cprint("${color.success}all plugins have been updated in ${bright}%s${clear}!", plugindir) end, catch { @@ -169,80 +279,61 @@ function _update() environment.leave() end --- remove the given installed plugin (from ~/.xmake/plugins/) +-- remove the given installed plugin function _remove() local name = assert(option.get("plugins"), "please specify the plugin name to be removed!") assert(name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name) - local plugindir = path.join(global.directory(), "plugins", name) - assert(os.isdir(plugindir), "plugin(%s) not found!", name) - os.rmdir(plugindir) - cprint("${color.success}remove plugin(%s) ok!", name) + local dir = _get_plugindir(name) + assert(os.isdir(dir), "plugin(%s) not found!", name) + os.rmdir(dir) + cprint("${color.success}remove ${bright}%s${clear} ok!", name) end --- list all installed plugins (manual + repository) +-- list all plugins function _list() local seen = {} - -- manually installed plugins - local plugindir = path.join(global.directory(), "plugins") - cprint("plugins in ${bright}%s${clear}:", plugindir) + + -- installed plugins + local plugindir = _get_plugindir() + cprint("${bright}the installed plugins:${clear}") local found = false for _, dir in ipairs(os.dirs(path.join(plugindir, "*")) or {}) do if os.isfile(path.join(dir, "xmake.lua")) then local name = path.filename(dir) seen[name] = true found = true - cprint(" ${color.dump.string}%s${clear}", name) + cprint(" ${bright}%s${clear}", name) end end if not found then print(" (none)") end - -- repository plugins (from scanned repos) - local reposdir = path.join(global.directory(), "repositories") - for _, dir in ipairs(os.dirs(path.join(reposdir, "*")) or {}) do - local rplugindir = path.join(dir, "plugins") + -- plugins available in repositories (not yet installed) + cprint("${bright}in xmake-repo:${clear}") + local avail = false + local repos = _repositories() + for _, repo in ipairs(repos) do + local rplugindir = path.join(repo:directory(), "plugins") if os.isdir(rplugindir) then - local reponame = path.filename(dir) - cprint("plugins in repository ${bright}%s${clear}:", reponame) - local repofound = false for _, subdir in ipairs(os.dirs(path.join(rplugindir, "*")) or {}) do - if os.isfile(path.join(subdir, "xmake.lua")) and not seen[path.filename(subdir)] then - seen[path.filename(subdir)] = true - repofound = true - cprint(" ${color.dump.string}%s${clear}", path.filename(subdir)) + local name = path.filename(subdir) + if os.isfile(path.join(subdir, "xmake.lua")) and not seen[name] then + seen[name] = true + avail = true + cprint(" - ${bright}%s${clear} ${dim}(run ${bright}xmake plugin --install %s${clear}${dim} to install)${clear}", name, name) end end - if not repofound then - print(" (none)") - end end end - - -- local checkout plugins - local repodir = os.getenv("XMAKE_MAIN_REPO") - if repodir and os.isdir(repodir) then - local rplugindir = path.join(repodir, "plugins") - if os.isdir(rplugindir) then - cprint("plugins in ${bright}XMAKE_MAIN_REPO${clear}:") - local repofound = false - for _, subdir in ipairs(os.dirs(path.join(rplugindir, "*")) or {}) do - if os.isfile(path.join(subdir, "xmake.lua")) and not seen[path.filename(subdir)] then - seen[path.filename(subdir)] = true - repofound = true - cprint(" ${color.dump.string}%s${clear}", path.filename(subdir)) - end - end - if not repofound then - print(" (none)") - end - end + if not avail then + print(" (none)") end end -- clear all installed plugins function _clear() - local plugindir = path.join(global.directory(), "plugins") + local plugindir = _get_plugindir() if os.isdir(plugindir) then os.rmdir(plugindir) end diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index 87d85e81d..a1b7f61ba 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -33,6 +33,10 @@ task("plugin") {nil, "plugins", "v", nil, "The plugins path, url or package name.", "e.g.", " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins", + " $ xmake plugin --install github:xmake-io/xmake-plugins", + " $ xmake plugin --install github:xmake-io/xmake-plugins#dev", + " $ xmake plugin --install /tmp/my-plugin", + " $ xmake plugin --install xmake-repo@hello-world", " $ xmake plugin --install hello-world", " $ xmake plugin --remove hello-world", " $ xmake plugin --list", -- cgit v1.3.1 From 235b82edd8b262a7080a9f9ee518567f5a54b836 Mon Sep 17 00:00:00 2001 From: Saikari Date: Mon, 27 Jul 2026 12:36:07 +0300 Subject: Enhance plugin installation by validating plugin names and improving error handling during installation and updates --- tests/plugins/repository/test.lua | 50 ++++--- xmake/plugins/plugin/main.lua | 266 ++++++++++++++++++++++---------------- xmake/plugins/plugin/xmake.lua | 2 +- 3 files changed, 188 insertions(+), 130 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/tests/plugins/repository/test.lua b/tests/plugins/repository/test.lua index bbff847a5..e0c67058b 100644 --- a/tests/plugins/repository/test.lua +++ b/tests/plugins/repository/test.lua @@ -19,13 +19,28 @@ function main() local repodir = os.tmpfile() .. ".plugins-repository" local localdir = path.join(os.tmpfile() .. ".local-plugin", local_name) local plugindir = path.join(global.directory(), "plugins") - local cachefile = path.join(global.directory(), "cache", "repository") + local cachefile = path.join(global.cachedir(), "repository") local cachebackup = os.tmpfile() .. ".repository" if os.isfile(cachefile) then os.cp(cachefile, cachebackup) end + local function cleanup() + for _, name in ipairs({hello_name, formatter_name, available_name, local_name}) do + os.tryrm(path.join(plugindir, name)) + end + if os.isfile(cachebackup) then + os.cp(cachebackup, cachefile) + else + os.tryrm(cachefile) + end + os.tryrm(cachebackup) + os.tryrm(repodir) + os.tryrm(path.directory(localdir)) + end + + try { function () @@ -33,17 +48,15 @@ function main() _write_plugin(path.join(repodir, "plugins", hello_name), hello_name, "repo-ok") _write_plugin(path.join(repodir, "plugins", formatter_name), formatter_name, "format-ok") _write_plugin(path.join(repodir, "plugins", available_name), available_name, "available-ok") - local cache = io.load(cachefile) or {} + os.mkdir(path.directory(cachefile)) + local cache = os.isfile(cachefile) and io.load(cachefile) or {} cache.repositories = cache.repositories or {} cache.repositories[reponame] = {repodir} io.save(cachefile, cache) - -- Feature: install by plain name from repository - os.exec("xmake plugin --install " .. hello_name) + -- Feature: install by plain name and repo@name in one invocation + os.exec("xmake plugin --install " .. hello_name .. " " .. reponame .. "@" .. formatter_name) os.exec("xmake " .. hello_name) - - -- Feature: install by repo@name format - os.exec("xmake plugin --install " .. reponame .. "@" .. formatter_name) os.exec("xmake " .. formatter_name) -- Feature: --list shows installed and available repository plugins @@ -69,21 +82,22 @@ function main() -- Feature: install non-existent plugin fails gracefully local ok = try { function () os.exec("xmake plugin --install plugin-test-missing-" .. suffix) end } assert(not ok) + -- Feature: reject plugin name traversal + ok = try { function () os.exec("xmake plugin --install " .. reponame .. "@..") end } + assert(not ok) + end, + catch + { + function (errors) + cleanup() + raise(errors) + end + }, finally { function () - for _, name in ipairs({hello_name, formatter_name, available_name, local_name}) do - os.tryrm(path.join(plugindir, name)) - end - if os.isfile(cachebackup) then - os.cp(cachebackup, cachefile) - else - os.tryrm(cachefile) - end - os.tryrm(cachebackup) - os.tryrm(repodir) - os.tryrm(path.directory(localdir)) + cleanup() end } } diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index a29ad8687..db03c7abb 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -26,10 +26,16 @@ import("devel.git") import("net.fasturl") import("private.action.require.impl.environment") +-- validate a plugin directory name +function _check_plugin_name(name) + assert(type(name) == "string" and name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name) + return name +end + -- get plugin directory in ~/.xmake/plugins function _get_plugindir(name) local plugindir = path.join(global.directory(), "plugins") - return name and path.join(plugindir, name) or plugindir + return name and path.join(plugindir, _check_plugin_name(name)) or plugindir end -- get local and global repositories, with local taking precedence @@ -39,22 +45,36 @@ end -- get plugin urls for batch install function _plugin_urls() - local urls = option.get("plugins") - if urls then - local result = {} - for _, url in ipairs(urls) do - table.insert(result, git.asgiturl(url) or url) - end - urls = result - else - urls = { - "https://github.com/xmake-io/xmake-plugins.git", - "https://gitlab.com/tboox/xmake-plugins.git", - "https://gitee.com/tboox/xmake-plugins.git"} - urls = fasturl.add(urls) - urls = fasturl.sort(urls) - end - return urls + local urls = { + "https://github.com/xmake-io/xmake-plugins.git", + "https://gitlab.com/tboox/xmake-plugins.git", + "https://gitee.com/tboox/xmake-plugins.git"} + fasturl.add(urls) + return fasturl.sort(urls) +end + +-- run a function with a temporary directory that is removed on success or failure +function _with_tmpdir(fn) + local tmpdir = os.tmpfile() .. ".dir" + return try + { + function () + return fn(tmpdir) + end, + catch + { + function (errors) + os.tryrm(tmpdir) + raise(errors) + end + }, + finally + { + function () + os.tryrm(tmpdir) + end + } + } end function _manifest_path() @@ -84,6 +104,7 @@ end -- install a plugin from the given repository or the first repository containing it function _install_plugins_from_repo(name, reponame) + _check_plugin_name(name) for _, repo in ipairs(_repositories()) do if not reponame or repo:name() == reponame then local srcdir = _find_plugin_in_repo(repo:directory(), name) @@ -124,32 +145,65 @@ function _install_from_git(url) end url = "https://github.com/" .. url .. ".git" end - local tmpdir = os.tmpfile() .. ".dir" - local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir} - if branch then - clone_opt.branch = branch - end - git.clone(git.asgiturl(url) or url, clone_opt) - local found = false - local function install(srcdir, name) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - found = true - end - if os.isfile(path.join(tmpdir, "xmake.lua")) then - install(tmpdir, path.basename(path.filename(url))) - else - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - install(srcdir, path.filename(srcdir)) + _with_tmpdir(function (tmpdir) + local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir} + if branch then + clone_opt.branch = branch + end + git.clone(git.asgiturl(url) or url, clone_opt) + local found = false + local function install(srcdir, name) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + found = true end + if os.isfile(path.join(tmpdir, "xmake.lua")) then + install(tmpdir, path.basename(path.filename(url))) + else + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + install(srcdir, path.filename(srcdir)) + end + end + if not found then + raise("no plugin found in %s", url) + end + end) +end + +-- install a single plugin +function _install_one(name) + -- parse repo@plugin format + local i = name:find("@", 1, true) + if i and not name:find("[/\\:]") then + local reponame = name:sub(1, i - 1) + local pluginname = name:sub(i + 1) + _install_plugins_from_repo(pluginname, reponame) + return end - os.tryrm(tmpdir) - if not found then - raise("no plugin found in %s", url) + + -- github shortcut: github:user/repo or github:user/repo#branch + if name:startswith("github:") then + _install_from_git(name) + return + end + + -- git url or local path + if name:startswith("file://") or git.asgiturl(name) then + _install_from_git(name) + return + elseif os.isdir(name) then + _install_from_local(name) + return + elseif name:find("[/\\:]") then + _install_from_git(name) + return end + + -- plain name: try to find it in repositories + _install_plugins_from_repo(name) end -- install plugins @@ -158,84 +212,69 @@ function _install() -- enter environment environment.enter() + local errors try { function () - -- do install - local name = option.get("plugins") - if name and #name > 0 then - -- parse repo@plugin format - local i = name:find("@", 1, true) - if i and not name:find("[/\\:]") then - local reponame = name:sub(1, i - 1) - local pluginname = name:sub(i + 1) - _install_plugins_from_repo(pluginname, reponame) - return - end - - -- github shortcut: github:user/repo or github:user/repo#branch - if name:startswith("github:") then - _install_from_git(name) - return - end - - -- git url or local path - if name:startswith("file://") or git.asgiturl(name) then - _install_from_git(name) - return - elseif os.isdir(name) then - _install_from_local(name) - return - elseif name:find("[/\\:]") then - _install_from_git(name) - return + -- install requested plugins + local names = option.get("plugins") + if names then + for _, name in ipairs(names) do + _install_one(name) end - - -- plain name: try to find it in repositories - _install_plugins_from_repo(name) return end -- do batch install from plugin collection urls local urls = _plugin_urls() - local tmpdir = os.tmpfile() .. ".dir" local plugindir = _get_plugindir() local installed_url - for _, url in ipairs(urls) do - cprint("installing plugins from ${bright}%s${clear} ..", url) - git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) - installed_url = url - break - end - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end - os.tryrm(tmpdir) - - if installed_url then - local manifest = _load_manifest() or {} - manifest.urls = manifest.urls or {} - table.join2(manifest.urls, installed_url) - _save_manifest(manifest) - end + _with_tmpdir(function (tmpdir) + for _, url in ipairs(urls) do + cprint("installing plugins from ${bright}%s${clear} ..", url) + local ok = try + { + function () + git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) + return true + end + } + if ok then + installed_url = url + break + end + os.tryrm(tmpdir) + end + assert(installed_url, "failed to install plugins from all urls!") + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + local name = path.filename(srcdir) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + end + end) + local manifest = _load_manifest() or {} + manifest.urls = manifest.urls or {} + table.join2(manifest.urls, installed_url) + _save_manifest(manifest) cprint("${color.success}all plugins have been installed in ${bright}%s${clear}!", plugindir) end, catch { - function (errors) - raise(errors) + function (_errors) + errors = _errors end } } -- leave environment environment.leave() + if errors then + raise(errors) + end end -- update plugins @@ -244,6 +283,7 @@ function _update() -- enter environment environment.enter() + local errors try { function () @@ -255,36 +295,40 @@ function _update() local plugindir = _get_plugindir() for _, url in ipairs(urls) do cprint("updating plugins from ${bright}%s${clear} ..", url) - local tmpdir = os.tmpfile() .. ".dir" - git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - os.tryrm(dstdir) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end - os.tryrm(tmpdir) + _with_tmpdir(function (tmpdir) + git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + local name = path.filename(srcdir) + local dstdir = _get_plugindir(name) + os.tryrm(dstdir) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + end + end) end cprint("${color.success}all plugins have been updated in ${bright}%s${clear}!", plugindir) end, catch { - function (errors) - raise(errors) + function (_errors) + errors = _errors end } } -- leave environment environment.leave() + if errors then + raise(errors) + end end -- remove the given installed plugin function _remove() - local name = assert(option.get("plugins"), "please specify the plugin name to be removed!") - assert(name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name) + local names = assert(option.get("plugins"), "please specify the plugin name to be removed!") + assert(#names == 1, "please specify only one plugin name to be removed!") + local name = _check_plugin_name(names[1]) local dir = _get_plugindir(name) assert(os.isdir(dir), "plugin(%s) not found!", name) os.rmdir(dir) diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index a1b7f61ba..aca6def8d 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -30,7 +30,7 @@ task("plugin") {'r', "remove", "k", nil, "Remove the given installed plugin."}, {'l', "list", "k", nil, "List all installed plugins."}, {'c', "clear", "k", nil, "Clear all installed plugins."}, - {nil, "plugins", "v", nil, "The plugins path, url or package name.", + {nil, "plugins", "vs", nil, "The plugin paths, urls or names.", "e.g.", " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins", " $ xmake plugin --install github:xmake-io/xmake-plugins", -- cgit v1.3.1 From 2127d0cef9be4eeab3e0192ab9bcd7aa36f9d658 Mon Sep 17 00:00:00 2001 From: Saikari Date: Tue, 28 Jul 2026 00:04:16 +0300 Subject: Refactor plugin installation logic to support custom plugin URLs and improve error handling during installation --- tests/plugins/repository/test.lua | 114 +++++++---------- xmake/plugins/plugin/main.lua | 259 ++++++++++++++++---------------------- xmake/plugins/plugin/xmake.lua | 2 +- 3 files changed, 160 insertions(+), 215 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/tests/plugins/repository/test.lua b/tests/plugins/repository/test.lua index e0c67058b..c4c6c4c20 100644 --- a/tests/plugins/repository/test.lua +++ b/tests/plugins/repository/test.lua @@ -1,3 +1,5 @@ +local global = import("core.base.global") + function _write_plugin(dir, name, text) io.writefile(path.join(dir, "xmake.lua"), string.format([[ task("%s") @@ -8,8 +10,7 @@ task("%s") io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], text)) end -function main() - local global = import("core.base.global") +function main(t) local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") local reponame = "plugin-test-repository-" .. suffix local hello_name = "plugin-test-hello-" .. suffix @@ -20,85 +21,66 @@ function main() local localdir = path.join(os.tmpfile() .. ".local-plugin", local_name) local plugindir = path.join(global.directory(), "plugins") local cachefile = path.join(global.cachedir(), "repository") - local cachebackup = os.tmpfile() .. ".repository" - - if os.isfile(cachefile) then - os.cp(cachefile, cachebackup) - end + -- reset test state local function cleanup() for _, name in ipairs({hello_name, formatter_name, available_name, local_name}) do os.tryrm(path.join(plugindir, name)) end - if os.isfile(cachebackup) then - os.cp(cachebackup, cachefile) - else - os.tryrm(cachefile) - end - os.tryrm(cachebackup) + local cache = os.isfile(cachefile) and io.load(cachefile) or {} + cache.repositories = cache.repositories or {} + cache.repositories[reponame] = nil + io.save(cachefile, cache) os.tryrm(repodir) os.tryrm(path.directory(localdir)) end + cleanup() + + -- mock repository with installed and available plugins + _write_plugin(path.join(repodir, "plugins", hello_name), hello_name, "repo-ok") + _write_plugin(path.join(repodir, "plugins", formatter_name), formatter_name, "format-ok") + _write_plugin(path.join(repodir, "plugins", available_name), available_name, "available-ok") + os.mkdir(path.directory(cachefile)) + local cache = os.isfile(cachefile) and io.load(cachefile) or {} + cache.repositories = cache.repositories or {} + cache.repositories[reponame] = {repodir} + io.save(cachefile, cache) + -- Feature: install by plain name from repository + os.runv("xmake", {"plugin", "--install", hello_name}) + os.runv("xmake", {hello_name}) - try - { - function () - -- mock repository with installed and available plugins - _write_plugin(path.join(repodir, "plugins", hello_name), hello_name, "repo-ok") - _write_plugin(path.join(repodir, "plugins", formatter_name), formatter_name, "format-ok") - _write_plugin(path.join(repodir, "plugins", available_name), available_name, "available-ok") - os.mkdir(path.directory(cachefile)) - local cache = os.isfile(cachefile) and io.load(cachefile) or {} - cache.repositories = cache.repositories or {} - cache.repositories[reponame] = {repodir} - io.save(cachefile, cache) + -- Feature: install by repo@name format + os.runv("xmake", {"plugin", "--install", reponame .. "@" .. formatter_name}) + os.runv("xmake", {formatter_name}) - -- Feature: install by plain name and repo@name in one invocation - os.exec("xmake plugin --install " .. hello_name .. " " .. reponame .. "@" .. formatter_name) - os.exec("xmake " .. hello_name) - os.exec("xmake " .. formatter_name) + -- Feature: --list shows installed and available repository plugins + local out = os.iorun("xmake plugin --list") + t:require(out:find(hello_name, 1, true)) + t:require(out:find(formatter_name, 1, true)) + t:require(out:find(available_name, 1, true)) + t:require(out:find("xmake plugin --install " .. available_name, 1, true)) - -- Feature: --list shows installed and available repository plugins - local out = os.iorun("xmake plugin --list") - assert(out:find(hello_name, 1, true)) - assert(out:find(formatter_name, 1, true)) - assert(out:find(available_name, 1, true)) - assert(out:find("xmake plugin --install " .. available_name, 1, true)) + -- Feature: install from local directory + _write_plugin(localdir, local_name, "local-ok") + os.runv("xmake", {"plugin", "--install", localdir}) + os.runv("xmake", {local_name}) - -- Feature: install from local directory - _write_plugin(localdir, local_name, "local-ok") - os.exec("xmake plugin --install " .. os.args(localdir)) - os.exec("xmake " .. local_name) + out = os.iorun("xmake plugin --list") + t:require(out:find(local_name, 1, true)) - out = os.iorun("xmake plugin --list") - assert(out:find(local_name, 1, true)) + -- Feature: remove plugin + os.runv("xmake", {"plugin", "--remove", local_name}) + out = os.iorun("xmake plugin --list") + t:require_not(out:find(local_name, 1, true)) - -- Feature: remove plugin - os.exec("xmake plugin --remove " .. local_name) - out = os.iorun("xmake plugin --list") - assert(not out:find(local_name, 1, true)) + -- Feature: install non-existent plugin fails gracefully + local ok = try { function () os.runv("xmake", {"plugin", "--install", "plugin-test-missing-" .. suffix}) return true end } + t:require_not(ok) - -- Feature: install non-existent plugin fails gracefully - local ok = try { function () os.exec("xmake plugin --install plugin-test-missing-" .. suffix) end } - assert(not ok) - -- Feature: reject plugin name traversal - ok = try { function () os.exec("xmake plugin --install " .. reponame .. "@..") end } - assert(not ok) + -- Feature: reject plugin name traversal + ok = try { function () os.runv("xmake", {"plugin", "--install", reponame .. "@.."}) return true end } + t:require_not(ok) - end, - catch - { - function (errors) - cleanup() - raise(errors) - end - }, - finally - { - function () - cleanup() - end - } - } + cleanup() end diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index db03c7abb..09de02983 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -45,36 +45,22 @@ end -- get plugin urls for batch install function _plugin_urls() - local urls = { - "https://github.com/xmake-io/xmake-plugins.git", - "https://gitlab.com/tboox/xmake-plugins.git", - "https://gitee.com/tboox/xmake-plugins.git"} - fasturl.add(urls) - return fasturl.sort(urls) -end - --- run a function with a temporary directory that is removed on success or failure -function _with_tmpdir(fn) - local tmpdir = os.tmpfile() .. ".dir" - return try - { - function () - return fn(tmpdir) - end, - catch - { - function (errors) - os.tryrm(tmpdir) - raise(errors) - end - }, - finally - { - function () - os.tryrm(tmpdir) - end - } - } + local urls = option.get("plugins") + if urls then + local result = {} + for _, url in ipairs(table.wrap(urls)) do + table.insert(result, git.asgiturl(url) or url) + end + urls = result + else + urls = { + "https://github.com/xmake-io/xmake-plugins.git", + "https://gitlab.com/tboox/xmake-plugins.git", + "https://gitee.com/tboox/xmake-plugins.git"} + fasturl.add(urls) + urls = fasturl.sort(urls) + end + return urls end function _manifest_path() @@ -143,67 +129,34 @@ function _install_from_git(url) branch = url:sub(i + 1) url = url:sub(1, i - 1) end - url = "https://github.com/" .. url .. ".git" + url = git.asgiturl("github:" .. url) end - _with_tmpdir(function (tmpdir) - local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir} - if branch then - clone_opt.branch = branch - end - git.clone(git.asgiturl(url) or url, clone_opt) - local found = false - local function install(srcdir, name) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - found = true - end - if os.isfile(path.join(tmpdir, "xmake.lua")) then - install(tmpdir, path.basename(path.filename(url))) - else - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - install(srcdir, path.filename(srcdir)) - end - end - if not found then - raise("no plugin found in %s", url) - end - end) -end - --- install a single plugin -function _install_one(name) - -- parse repo@plugin format - local i = name:find("@", 1, true) - if i and not name:find("[/\\:]") then - local reponame = name:sub(1, i - 1) - local pluginname = name:sub(i + 1) - _install_plugins_from_repo(pluginname, reponame) - return + local tmpdir = os.tmpfile() .. ".dir" + local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir} + if branch then + clone_opt.branch = branch end - - -- github shortcut: github:user/repo or github:user/repo#branch - if name:startswith("github:") then - _install_from_git(name) - return + git.clone(git.asgiturl(url) or url, clone_opt) + local found = false + local function install(srcdir, name) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + found = true end - - -- git url or local path - if name:startswith("file://") or git.asgiturl(name) then - _install_from_git(name) - return - elseif os.isdir(name) then - _install_from_local(name) - return - elseif name:find("[/\\:]") then - _install_from_git(name) - return + if os.isfile(path.join(tmpdir, "xmake.lua")) then + install(tmpdir, path.basename(path.filename(url))) + else + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + install(srcdir, path.filename(srcdir)) + end + end + os.tryrm(tmpdir) + if not found then + raise("no plugin found in %s", url) end - - -- plain name: try to find it in repositories - _install_plugins_from_repo(name) end -- install plugins @@ -212,69 +165,84 @@ function _install() -- enter environment environment.enter() - local errors try { function () - -- install requested plugins - local names = option.get("plugins") - if names then - for _, name in ipairs(names) do - _install_one(name) + -- do install + local name = option.get("plugins") + if name and #name > 0 then + -- parse repo@plugin format + local i = name:find("@", 1, true) + if i and not name:find("[/\\:]") then + local reponame = name:sub(1, i - 1) + local pluginname = name:sub(i + 1) + _install_plugins_from_repo(pluginname, reponame) + return end + + -- github shortcut: github:user/repo or github:user/repo#branch + if name:startswith("github:") then + _install_from_git(name) + return + end + + -- git url or local path + if name:startswith("file://") or git.asgiturl(name) then + _install_from_git(name) + return + elseif os.isdir(name) then + _install_from_local(name) + return + elseif name:find("[/\\:]") then + _install_from_git(name) + return + end + + -- plain name: try to find it in repositories + _install_plugins_from_repo(name) return end -- do batch install from plugin collection urls local urls = _plugin_urls() + local tmpdir = os.tmpfile() .. ".dir" local plugindir = _get_plugindir() local installed_url - _with_tmpdir(function (tmpdir) - for _, url in ipairs(urls) do - cprint("installing plugins from ${bright}%s${clear} ..", url) - local ok = try - { - function () - git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) - return true - end - } - if ok then - installed_url = url - break - end - os.tryrm(tmpdir) - end - assert(installed_url, "failed to install plugins from all urls!") - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end - end) - local manifest = _load_manifest() or {} - manifest.urls = manifest.urls or {} - table.join2(manifest.urls, installed_url) - _save_manifest(manifest) + for _, url in ipairs(urls) do + cprint("installing plugins from ${bright}%s${clear} ..", url) + git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) + installed_url = url + break + end + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + local name = path.filename(srcdir) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + end + os.tryrm(tmpdir) + + if installed_url then + local manifest = _load_manifest() or {} + manifest.urls = manifest.urls or {} + table.join2(manifest.urls, installed_url) + _save_manifest(manifest) + end cprint("${color.success}all plugins have been installed in ${bright}%s${clear}!", plugindir) end, catch { - function (_errors) - errors = _errors + function (errors) + raise(errors) end } } -- leave environment environment.leave() - if errors then - raise(errors) - end end -- update plugins @@ -283,7 +251,6 @@ function _update() -- enter environment environment.enter() - local errors try { function () @@ -295,40 +262,36 @@ function _update() local plugindir = _get_plugindir() for _, url in ipairs(urls) do cprint("updating plugins from ${bright}%s${clear} ..", url) - _with_tmpdir(function (tmpdir) - git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - os.tryrm(dstdir) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end - end) + local tmpdir = os.tmpfile() .. ".dir" + git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + local name = path.filename(srcdir) + local dstdir = _get_plugindir(name) + os.tryrm(dstdir) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + end + os.tryrm(tmpdir) end cprint("${color.success}all plugins have been updated in ${bright}%s${clear}!", plugindir) end, catch { - function (_errors) - errors = _errors + function (errors) + raise(errors) end } } -- leave environment environment.leave() - if errors then - raise(errors) - end end -- remove the given installed plugin function _remove() - local names = assert(option.get("plugins"), "please specify the plugin name to be removed!") - assert(#names == 1, "please specify only one plugin name to be removed!") - local name = _check_plugin_name(names[1]) + local name = assert(option.get("plugins"), "please specify the plugin name to be removed!") + assert(name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name) local dir = _get_plugindir(name) assert(os.isdir(dir), "plugin(%s) not found!", name) os.rmdir(dir) diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index aca6def8d..a1b7f61ba 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -30,7 +30,7 @@ task("plugin") {'r', "remove", "k", nil, "Remove the given installed plugin."}, {'l', "list", "k", nil, "List all installed plugins."}, {'c', "clear", "k", nil, "Clear all installed plugins."}, - {nil, "plugins", "vs", nil, "The plugin paths, urls or names.", + {nil, "plugins", "v", nil, "The plugins path, url or package name.", "e.g.", " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins", " $ xmake plugin --install github:xmake-io/xmake-plugins", -- cgit v1.3.1 From 77980687c5c1a4102d29e7c314cb9c23f98feab9 Mon Sep 17 00:00:00 2001 From: Saikari Date: Wed, 29 Jul 2026 01:48:24 +0300 Subject: Address review comments: refine plugin removal, multi-URL install, and list display --- xmake/plugins/plugin/main.lua | 71 ++++++++++++++++++++++++------------------ xmake/plugins/plugin/xmake.lua | 2 +- 2 files changed, 41 insertions(+), 32 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index 002bac72c..515c5c6c4 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -143,6 +143,36 @@ function _install_from_git(url) os.tryrm(tmpdir) end +-- install a single plugin +function _install_one(name) + -- parse repo@plugin format + local i = name:find("@", 1, true) + if i and not name:find("[/\\:]") then + local reponame = name:sub(1, i - 1) + local pluginname = name:sub(i + 1) + _install_plugins_from_repo(pluginname, reponame) + return + end + + -- github shortcut: github:user/repo or github:user/repo#branch + if name:startswith("github:") then + _install_from_git(name) + return + end + + -- git url or local path + 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 + _install_plugins_from_repo(name) +end + -- install plugins function _install() @@ -153,38 +183,16 @@ function _install() { function () - -- do install - local name = option.get("plugins") - if name and #name > 0 then - -- parse repo@plugin format - local i = name:find("@", 1, true) - if i and not name:find("[/\\:]") then - local reponame = name:sub(1, i - 1) - local pluginname = name:sub(i + 1) - _install_plugins_from_repo(pluginname, reponame) - return - end - - -- github shortcut: github:user/repo or github:user/repo#branch - if name:startswith("github:") then - _install_from_git(name) - return + -- install requested plugins + local names = option.get("plugins") + if names then + for _, name in ipairs(names) do + _install_one(name) end - - -- git url or local path - 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 - _install_plugins_from_repo(name) return end + -- do batch install from plugin collection urls local urls = _plugin_urls() local tmpdir = os.tmpfile() .. ".dir" @@ -271,8 +279,9 @@ end -- remove the given installed plugin function _remove() - local name = assert(option.get("plugins"), "please specify the plugin name to be removed!") - assert(name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name) + local names = assert(option.get("plugins"), "please specify the plugin name to be removed!") + assert(#names == 1, "please specify only one plugin name to be removed!") + local name = names[1] local dir = _get_plugindir(name) assert(os.isdir(dir), "plugin(%s) not found!", name) os.rmdir(dir) @@ -300,7 +309,7 @@ function _list() end -- plugins available in repositories (not yet installed) - cprint("${bright}in xmake-repo:${clear}") + cprint("${bright}available in configured repositories:${clear}") local avail = false local repos = _repositories() for _, repo in ipairs(repos) do diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index a1b7f61ba..aca6def8d 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -30,7 +30,7 @@ task("plugin") {'r', "remove", "k", nil, "Remove the given installed plugin."}, {'l', "list", "k", nil, "List all installed plugins."}, {'c', "clear", "k", nil, "Clear all installed plugins."}, - {nil, "plugins", "v", nil, "The plugins path, url or package name.", + {nil, "plugins", "vs", nil, "The plugin paths, urls or names.", "e.g.", " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins", " $ xmake plugin --install github:xmake-io/xmake-plugins", -- cgit v1.3.1 From f91724eb1d7f80e81772ad6f20b03b2a0cff8125 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 1 Aug 2026 08:36:13 +0800 Subject: remove old plugin repo --- xmake/plugins/plugin/main.lua | 150 +++++------------------------------------ xmake/plugins/plugin/xmake.lua | 4 +- 2 files changed, 17 insertions(+), 137 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index a21b4b264..48bae9cde 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -23,7 +23,6 @@ import("core.base.option") import("core.base.global") import("core.package.repository") import("devel.git") -import("net.fasturl") import("private.action.require.impl.environment") -- validate a plugin directory name @@ -43,43 +42,6 @@ function _repositories() return table.join(repository.repositories({global = false}), repository.repositories({global = true})) end --- get plugin urls for batch install -function _plugin_urls() - local urls = option.get("plugins") - if urls then - local result = {} - for _, url in ipairs(urls) do - table.insert(result, git.asgiturl(url) or url) - end - urls = result - else - urls = { - "https://github.com/xmake-io/xmake-plugins.git", - "https://gitlab.com/tboox/xmake-plugins.git", - "https://gitee.com/tboox/xmake-plugins.git"} - fasturl.add(urls) - urls = fasturl.sort(urls) - end - return urls -end - -function _manifest_path() - return path.join(_get_plugindir(), "manifest.txt") -end - --- load manifest -function _load_manifest() - local manifest_path = _manifest_path() - if os.isfile(manifest_path) then - return io.load(manifest_path) - end -end - --- save manifest -function _save_manifest(manifest) - io.save(_manifest_path(), manifest) -end - -- find a plugin directory in the given repository directory -- -- plugins in a repository follow the same layout as packages: @@ -135,14 +97,7 @@ function _install_from_git(url) end local tmpdir = os.tmpfile() .. ".dir" git.clone(url, {verbose = option.get("verbose"), branch = branch, outputdir = tmpdir}) - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end + _copy_plugins_from_dir(tmpdir) os.tryrm(tmpdir) end @@ -176,97 +131,28 @@ function _install_one(name) _install_plugins_from_repo(name) end --- install plugins -function _install() - - -- enter environment - environment.enter() - - try - { - function () - - -- install requested plugins - local names = option.get("plugins") - if names then - for _, name in ipairs(names) do - _install_one(name) - end - return - end - - - -- do batch install from plugin collection urls - local urls = _plugin_urls() - local tmpdir = os.tmpfile() .. ".dir" - local plugindir = _get_plugindir() - local installed_url - for _, url in ipairs(urls) do - cprint("installing plugins from ${bright}%s${clear} ..", url) - git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) - installed_url = url - break - end - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end - os.tryrm(tmpdir) - - if installed_url then - local manifest = _load_manifest() or {} - manifest.urls = manifest.urls or {} - table.join2(manifest.urls, installed_url) - _save_manifest(manifest) - end - cprint("${color.success}all plugins have been installed in ${bright}%s${clear}!", plugindir) - end, - catch - { - function (errors) - raise(errors) - end - } - } - - -- leave environment - environment.leave() +-- copy every plugin found under the cloned directory into the plugins directory +function _copy_plugins_from_dir(tmpdir) + for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do + local srcdir = path.directory(filepath) + local name = path.filename(srcdir) + local dstdir = _get_plugindir(name) + assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) + os.vcp(srcdir, dstdir) + cprint(" ${color.success}-> ${bright}%s${clear}", name) + end end --- update plugins -function _update() - - -- enter environment +-- install plugins +function _install() + local names = assert(option.get("plugins"), "please specify the plugins to be installed!") environment.enter() - try { function () - - -- do update - local manifest = _load_manifest() - assert(manifest and manifest.urls, "3rd plugins not found!") - local urls = manifest.urls - local plugindir = _get_plugindir() - for _, url in ipairs(urls) do - cprint("updating plugins from ${bright}%s${clear} ..", url) - local tmpdir = os.tmpfile() .. ".dir" - git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir}) - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - os.tryrm(dstdir) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end - os.tryrm(tmpdir) + for _, name in ipairs(names) do + _install_one(name) end - cprint("${color.success}all plugins have been updated in ${bright}%s${clear}!", plugindir) end, catch { @@ -275,8 +161,6 @@ function _update() end } } - - -- leave environment environment.leave() end @@ -400,8 +284,6 @@ end function main() if option.get("install") then _install() - elseif option.get("update") then - _update() elseif option.get("remove") then _remove() elseif option.get("list") then diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index aca6def8d..8da06a92e 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -26,7 +26,6 @@ task("plugin") description = "Manage plugins of xmake.", options = { {'i', "install", "k", nil, "Install plugins."}, - {'u', "update", "k", nil, "Update plugins."}, {'r', "remove", "k", nil, "Remove the given installed plugin."}, {'l', "list", "k", nil, "List all installed plugins."}, {'c', "clear", "k", nil, "Clear all installed plugins."}, @@ -39,7 +38,6 @@ task("plugin") " $ xmake plugin --install xmake-repo@hello-world", " $ xmake plugin --install hello-world", " $ xmake plugin --remove hello-world", - " $ xmake plugin --list", - " $ xmake plugin --update"} + " $ xmake plugin --list"} } } -- cgit v1.3.1 From 8d7a74306556a4c292c0345f1a9aa833116ebcec Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 1 Aug 2026 08:53:30 +0800 Subject: improve tests --- tests/plugins/repository/test.lua | 135 +++++++++++++++++++++----------------- xmake/plugins/plugin/main.lua | 26 +++----- xmake/plugins/plugin/xmake.lua | 12 ++-- 3 files changed, 90 insertions(+), 83 deletions(-) (limited to 'xmake/plugins/plugin/xmake.lua') diff --git a/tests/plugins/repository/test.lua b/tests/plugins/repository/test.lua index 4f1347317..f1cfba8c7 100644 --- a/tests/plugins/repository/test.lua +++ b/tests/plugins/repository/test.lua @@ -1,88 +1,105 @@ import("core.base.global") -function _write_plugin(dir, name, text) +-- 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"} -]], name, name)) - io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], text)) + 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 -function main(t) +-- create a temporary plugin repository (packages layout: plugins//) and register it +-- +-- @return reponame, names, cleanup +function _mock_repo(basenames) local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") - local reponame = "plugin-test-repository-" .. suffix - local hello_name = "plugin-test-hello-" .. suffix - local formatter_name = "plugin-test-formatter-" .. suffix - local available_name = "plugin-test-available-" .. suffix - local local_name = "plugin-test-local-" .. suffix - local repodir = os.tmpfile() .. ".plugins-repository" - local localdir = path.join(os.tmpfile() .. ".local-plugin", local_name) - local plugindir = path.join(global.directory(), "plugins") + 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(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) - -- reset test state local function cleanup() - for _, name in ipairs({hello_name, formatter_name, available_name, local_name}) do - os.tryrm(path.join(plugindir, name)) + 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 {} - cache.repositories = cache.repositories or {} - cache.repositories[reponame] = nil + if cache.repositories then + cache.repositories[reponame] = nil + end io.save(cachefile, cache) os.tryrm(repodir) - os.tryrm(path.directory(localdir)) end - cleanup() + return reponame, names, cleanup +end - -- mock repository with installed and available plugins (packages-like layout: plugins//) - _write_plugin(path.join(repodir, "plugins", hello_name:sub(1, 1), hello_name), hello_name, "repo-ok") - _write_plugin(path.join(repodir, "plugins", formatter_name:sub(1, 1), formatter_name), formatter_name, "format-ok") - _write_plugin(path.join(repodir, "plugins", available_name:sub(1, 1), available_name), available_name, "available-ok") - os.mkdir(path.directory(cachefile)) - local cache = os.isfile(cachefile) and io.load(cachefile) or {} - cache.repositories = cache.repositories or {} - cache.repositories[reponame] = {repodir} - io.save(cachefile, cache) +-- 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] - -- Feature: install by plain name from repository - os.runv("xmake", {"plugin", "--install", hello_name}) - os.runv("xmake", {hello_name}) + -- install by plain name (searched across all repositories) + os.runv("xmake", {"plugin", "--install", name}) + t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - -- Feature: install by repo@name format - os.runv("xmake", {"plugin", "--install", reponame .. "@" .. formatter_name}) - os.runv("xmake", {formatter_name}) + -- reinstall by repo@name + os.runv("xmake", {"plugin", "--remove", name}) + os.runv("xmake", {"plugin", "--install", reponame .. "@" .. name}) + t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - -- Feature: --list shows installed and available repository plugins - local out = os.iorun("xmake plugin --list") - t:require(out:find("the built-in plugins:", 1, true)) - t:require(out:find("project", 1, true)) - t:require(out:find(hello_name, 1, true)) - t:require(out:find(formatter_name, 1, true)) - t:require(out:find(available_name, 1, true)) - t:require(out:find("xmake plugin --install " .. available_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) - -- Feature: install from local directory - _write_plugin(localdir, local_name, "local-ok") - os.runv("xmake", {"plugin", "--install", localdir}) - os.runv("xmake", {local_name}) + os.runv("xmake", {"plugin", "--install", dir}) + t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - out = os.iorun("xmake plugin --list") - t:require(out:find(local_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 }) - -- Feature: remove plugin - os.runv("xmake", {"plugin", "--remove", local_name}) - out = os.iorun("xmake plugin --list") - t:require_not(out:find(local_name, 1, true)) + os.tryrm(path.directory(dir)) +end - -- Feature: install non-existent plugin fails gracefully - local ok = try { function () os.runv("xmake", {"plugin", "--install", "plugin-test-missing-" .. suffix}) return true end } - t:require_not(ok) +-- --list shows the built-in, installed and available plugins +function test_list(t) + local reponame, names, cleanup = _mock_repo({"hello", "world"}) - -- Feature: reject plugin name traversal - ok = try { function () os.runv("xmake", {"plugin", "--install", reponame .. "@.."}) return true end } - t:require_not(ok) + -- install the first plugin, leave the second only available + os.runv("xmake", {"plugin", "--install", 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", "plugin-test-missing"}); return true end }) + t:require_not(try { function () os.runv("xmake", {"plugin", "--install", "somerepo@.."}); return true end }) +end diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua index 48bae9cde..5158352c0 100644 --- a/xmake/plugins/plugin/main.lua +++ b/xmake/plugins/plugin/main.lua @@ -74,17 +74,17 @@ function _install_plugins_from_repo(name, reponame) raise("plugin(%s): not found in any repository! try ${bright}xrepo update-repo${clear} first.", name) end --- install a plugin from a local directory -function _install_from_local(dir) +-- install a single plugin from a source directory (as the given name, default to the directory name) +function _install_from_local(dir, name) assert(os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")), "plugin path(%s): ${bright}xmake.lua${clear} not found!", dir) - local name = path.filename(path.absolute(dir)) + name = name or path.filename(path.absolute(dir)) local dstdir = _get_plugindir(name) assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) os.vcp(dir, dstdir) - cprint("${color.success}install ${bright}%s${clear} from ${bright}%s${clear} ok!", name, dir) + cprint("${color.success}install ${bright}%s${clear} ok!", name) end --- install a plugin from a git url or github shortcut +-- install a single plugin from a git url or github shortcut, e.g. https://github.com/xmake-io/hello-world function _install_from_git(url) local branch if url:startswith("github:") then @@ -95,9 +95,11 @@ function _install_from_git(url) end url = git.asgiturl(url) end + local name = (path.filename(url):gsub("%.git$", "")) local tmpdir = os.tmpfile() .. ".dir" git.clone(url, {verbose = option.get("verbose"), branch = branch, outputdir = tmpdir}) - _copy_plugins_from_dir(tmpdir) + os.tryrm(path.join(tmpdir, ".git")) + _install_from_local(tmpdir, name) os.tryrm(tmpdir) end @@ -131,18 +133,6 @@ function _install_one(name) _install_plugins_from_repo(name) end --- copy every plugin found under the cloned directory into the plugins directory -function _copy_plugins_from_dir(tmpdir) - for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do - local srcdir = path.directory(filepath) - local name = path.filename(srcdir) - local dstdir = _get_plugindir(name) - assert(not os.isdir(dstdir), "plugin(%s) already exists!", name) - os.vcp(srcdir, dstdir) - cprint(" ${color.success}-> ${bright}%s${clear}", name) - end -end - -- install plugins function _install() local names = assert(option.get("plugins"), "please specify the plugins to be installed!") diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua index 8da06a92e..4aab3daa4 100644 --- a/xmake/plugins/plugin/xmake.lua +++ b/xmake/plugins/plugin/xmake.lua @@ -31,13 +31,13 @@ task("plugin") {'c', "clear", "k", nil, "Clear all installed plugins."}, {nil, "plugins", "vs", nil, "The plugin paths, urls or names.", "e.g.", - " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins", - " $ xmake plugin --install github:xmake-io/xmake-plugins", - " $ xmake plugin --install github:xmake-io/xmake-plugins#dev", + " $ xmake plugin --install https://github.com/myrepo/hello", + " $ xmake plugin --install github:myrepo/hello", + " $ xmake plugin --install github:myrepo/hello#dev", " $ xmake plugin --install /tmp/my-plugin", - " $ xmake plugin --install xmake-repo@hello-world", - " $ xmake plugin --install hello-world", - " $ xmake plugin --remove hello-world", + " $ xmake plugin --install xmake-repo@hello", + " $ xmake plugin --install hello", + " $ xmake plugin --remove hello", " $ xmake plugin --list"} } } -- cgit v1.3.1