summaryrefslogtreecommitdiff
path: root/xmake/plugins/plugin
diff options
context:
space:
mode:
authorruki <[email protected]>2026-08-02 00:24:14 +0800
committerGitHub <[email protected]>2026-08-02 00:24:14 +0800
commit33ed4e501281de51b2b8dcfa442b176f0dd6d2d2 (patch)
treef60409df69b8e5f3de5e43bbfef4d689233482fd /xmake/plugins/plugin
parentb41ed3acf227011f4904d95a439a30be9580e5cf (diff)
parent8d7a74306556a4c292c0345f1a9aa833116ebcec (diff)
Merge pull request #7680 from xmake-io/plugin
Support for plugin distribution based on xmake-repo
Diffstat (limited to 'xmake/plugins/plugin')
-rw-r--r--xmake/plugins/plugin/main.lua311
-rw-r--r--xmake/plugins/plugin/xmake.lua15
2 files changed, 219 insertions, 107 deletions
diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua
index b362381e4..5158352c0 100644
--- a/xmake/plugins/plugin/main.lua
+++ b/xmake/plugins/plugin/main.lua
@@ -21,87 +21,128 @@
-- 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
-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)
+-- 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, _check_plugin_name(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
+
+-- find a plugin directory in the given repository directory
+--
+-- plugins in a repository follow the same layout as packages:
+-- <repodir>/plugins/<first-letter>/<name>/xmake.lua
+function _find_plugin_in_repo(repodir, name)
+ local dir = path.join(repodir, "plugins", name:sub(1, 1):lower(), name)
+ if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then
+ return dir
+ end
+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)
+ 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
- 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
+ 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
--- get manifest path
-function _manifest_path()
- return path.join(global.directory(), "plugins", "manifest.txt")
+-- 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)
+ 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} ok!", name)
end
--- load manifest
-function _load_manifest()
- local manifest_path = _manifest_path()
- if os.isfile(manifest_path) then
- return io.load(manifest_path)
+-- 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
+ local i = url:find("#", 1, true)
+ if i then
+ branch = url:sub(i + 1)
+ url = url:sub(1, i - 1)
+ 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})
+ os.tryrm(path.join(tmpdir, ".git"))
+ _install_from_local(tmpdir, name)
+ os.tryrm(tmpdir)
end
--- save manifest
-function _save_manifest(manifest)
- io.save(_manifest_path(), manifest)
+-- 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()
-
- -- enter environment
+ local names = assert(option.get("plugins"), "please specify the plugins to be installed!")
environment.enter()
-
try
{
function ()
-
- -- do install
- local urls = _plugin_urls()
- local tmpdir = os.tmpfile() .. ".dir"
- local plugindir = path.join(global.directory(), "plugins")
- local installed_url
- for _, url in ipairs(urls) do
- cprint("installing plugins from %s ..", url)
- git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
- installed_url = url
- break
+ for _, name in ipairs(names) do
+ _install_one(name)
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 = path.join(plugindir, name)
- assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
- os.vcp(srcdir, dstdir)
- cprint(" ${yellow}->${clear} %s", 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)
end,
catch
{
@@ -110,57 +151,120 @@ function _install()
end
}
}
-
- -- leave environment
environment.leave()
end
--- update plugins
-function _update()
-
- -- enter environment
- environment.enter()
+-- 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 = names[1]
+ 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
- try
- {
- function ()
+-- get the description of a plugin from its xmake.lua menu
+function _plugin_description(dir)
+ local filepath = path.join(dir, "xmake.lua")
+ if os.isfile(filepath) then
+ local content = io.readfile(filepath)
+ if content then
+ return content:match("description%s*=%s*\"(.-)\"")
+ end
+ end
+end
- -- do 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")
- for _, url in ipairs(urls) do
- cprint("updating plugins from %s ..", 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)
- os.tryrm(dstdir)
- os.vcp(srcdir, dstdir)
- cprint(" ${yellow}->${clear} %s", name)
- end
- os.tryrm(tmpdir)
- end
- cprint("${bright}all plugins have been updated in %s!", plugindir)
- end,
- catch
- {
- function (errors)
- raise(errors)
+-- collect plugins (each subdirectory containing xmake.lua) under the given root
+--
+-- built-in/installed plugins are flat (<root>/<name>), while plugins in a
+-- repository follow the packages layout (<root>/<first-letter>/<name>)
+function _collect_plugins(root, seen, nested)
+ local entries = {}
+ local pattern = nested and path.join(root, "*", "*") or path.join(root, "*")
+ for _, dir in ipairs(os.dirs(pattern) or {}) do
+ local name = path.filename(dir)
+ if os.isfile(path.join(dir, "xmake.lua")) and (not seen or not seen[name]) then
+ if seen then
+ seen[name] = true
end
- }
- }
+ table.insert(entries, {name = name, description = _plugin_description(dir)})
+ end
+ end
+ return entries
+end
- -- leave environment
- environment.leave()
+-- print a plugin entry with its description aligned on the right
+function _print_plugin(name, description, width, note)
+ local suffix = description or ""
+ if note then
+ suffix = suffix ~= "" and (suffix .. " " .. note) or note
+ end
+ if suffix ~= "" then
+ local padding = math.max(width - #name, 1)
+ cprint(" ${color.dump.string}%s${clear}%s%s", name, (" "):rep(padding), suffix)
+ else
+ cprint(" ${color.dump.string}%s${clear}", name)
+ end
+end
+
+-- list all plugins
+function _list()
+ local seen = {}
+
+ -- collect plugins from every source
+ local builtin = _collect_plugins(path.join(os.programdir(), "plugins"), seen)
+ local plugindir = _get_plugindir()
+ local installed = _collect_plugins(plugindir, seen)
+ local avail = {}
+ for _, repo in ipairs(_repositories()) do
+ table.join2(avail, _collect_plugins(path.join(repo:directory(), "plugins"), seen, true))
+ end
+
+ -- compute the alignment width from all plugin names
+ local width = 0
+ for _, entries in ipairs({builtin, installed, avail}) do
+ for _, entry in ipairs(entries) do
+ width = math.max(width, #entry.name + 4)
+ end
+ end
+
+ -- built-in plugins
+ cprint("${bright}the built-in plugins:${clear}")
+ if #builtin > 0 then
+ for _, entry in ipairs(builtin) do
+ _print_plugin(entry.name, entry.description, width)
+ end
+ else
+ print(" (none)")
+ end
+
+ -- installed plugins
+ cprint("${bright}the installed plugins:${clear}")
+ if #installed > 0 then
+ for _, entry in ipairs(installed) do
+ _print_plugin(entry.name, entry.description, width)
+ end
+ else
+ print(" (none)")
+ end
+
+ -- plugins available in repositories (not yet installed)
+ cprint("${bright}available in configured repositories:${clear}")
+ if #avail > 0 then
+ for _, entry in ipairs(avail) do
+ local note = string.format("(run xmake plugin --install %s to install)", entry.name)
+ _print_plugin(entry.name, entry.description, width, note)
+ end
+ else
+ 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
@@ -170,10 +274,11 @@ 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
+ _list()
elseif option.get("clear") then
_clear()
end
end
-
diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua
index 05d9e3631..4aab3daa4 100644
--- a/xmake/plugins/plugin/xmake.lua
+++ b/xmake/plugins/plugin/xmake.lua
@@ -26,11 +26,18 @@ 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."},
- {nil, "plugins", "v", nil, "The plugins path or url.",
+ {nil, "plugins", "vs", nil, "The plugin paths, urls or names.",
"e.g.",
- " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins",
- " $ xmake plugin --update"}
+ " $ 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",
+ " $ xmake plugin --install hello",
+ " $ xmake plugin --remove hello",
+ " $ xmake plugin --list"}
}
}