diff options
| author | ruki <[email protected]> | 2026-08-02 00:24:14 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-08-02 00:24:14 +0800 |
| commit | 33ed4e501281de51b2b8dcfa442b176f0dd6d2d2 (patch) | |
| tree | f60409df69b8e5f3de5e43bbfef4d689233482fd /xmake/plugins/plugin | |
| parent | b41ed3acf227011f4904d95a439a30be9580e5cf (diff) | |
| parent | 8d7a74306556a4c292c0345f1a9aa833116ebcec (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.lua | 311 | ||||
| -rw-r--r-- | xmake/plugins/plugin/xmake.lua | 15 |
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"} } } |
