summaryrefslogtreecommitdiff
path: root/xmake/actions/addon/main.lua
diff options
context:
space:
mode:
authorruki <[email protected]>2026-08-08 18:34:44 +0800
committerruki <[email protected]>2026-08-09 18:25:42 +0800
commit6a024e442cf6e3a1fe55ab63b3385be592bb1e1e (patch)
tree82919c0bcb6aec321a12af5860c894b373e3256d /xmake/actions/addon/main.lua
parent53075cd271268c1931a41648a92891c362f2483a (diff)
move addon as action
Diffstat (limited to 'xmake/actions/addon/main.lua')
-rw-r--r--xmake/actions/addon/main.lua274
1 files changed, 274 insertions, 0 deletions
diff --git a/xmake/actions/addon/main.lua b/xmake/actions/addon/main.lua
new file mode 100644
index 000000000..d072cec7f
--- /dev/null
+++ b/xmake/actions/addon/main.lua
@@ -0,0 +1,274 @@
+--!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.package.addon")
+import("core.package.repository")
+import("devel.git")
+import("private.action.require.impl.environment")
+
+-- the version directory name for the addons installed from git urls or local directories
+local LOCALVERSION = "latest"
+
+-- validate an addon directory name
+function _check_addon_name(name)
+ assert(type(name) == "string" and name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid addon name(%s)!", name)
+ return name
+end
+
+-- get addon directory in ~/.xmake/addons
+function _get_addondir(name, version)
+ local addondir = addon.installdir()
+ if name then
+ addondir = path.join(addondir, addon.dirname(_check_addon_name(name)))
+ if version then
+ addondir = path.join(addondir, version)
+ end
+ end
+ return addondir
+end
+
+-- get local and global repositories, with local taking precedence
+function _repositories()
+ return table.join(repository.repositories({global = false}), repository.repositories({global = true}))
+end
+
+-- install an addon from the given repository or the first repository containing it
+function _install_from_repo(name, reponame)
+
+ -- check addon name
+ _check_addon_name(name)
+
+ -- do install
+ local installname = name
+ if reponame then
+ installname = reponame .. "@" .. name
+ end
+ local argv = {"lua", "private.xrepo", "install", "--addon"}
+ -- we need to pass the common options to the sub-process, e.g. -y, -v, -D
+ 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, installname)
+ os.execv(os.programfile(), argv)
+end
+
+-- install a single addon from a source directory (as the given name, default to the directory name)
+function _install_from_local(dir, name)
+ assert(os.isdir(dir), "addon path(%s) not found!", dir)
+ assert(#addon.payloads_of(dir) > 0, "addon path(%s): no payload directory found, e.g. ${bright}plugins${clear}!", dir)
+ name = name or path.filename(path.absolute(dir))
+ local dstdir = _get_addondir(name, LOCALVERSION)
+ assert(not os.isdir(dstdir), "addon(%s) already exists!", name)
+ os.vcp(dir, dstdir)
+ addon.register(name, LOCALVERSION)
+ cprint("${color.success}install ${bright}%s${clear} ok!", name)
+end
+
+-- install a single addon from a git url or github shortcut, e.g. https://github.com/xmake-addons/serial-monitor
+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
+
+-- install a single addon
+function _install_one(name)
+ -- parse repo@addon format
+ local i = name:find("@", 1, true)
+ if i and not name:find("[/\\:]") then
+ local reponame = name:sub(1, i - 1)
+ local addonname = name:sub(i + 1)
+ _install_from_repo(addonname, 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_from_repo(name)
+end
+
+-- install addons
+function _install()
+ local names = assert(option.get("addons"), "please specify the addons to be installed!")
+ environment.enter()
+ for _, name in ipairs(names) do
+ _install_one(name)
+ end
+ environment.leave()
+end
+
+-- remove the given installed addon
+function _remove()
+ local names = assert(option.get("addons"), "please specify the addon name to be removed!")
+ assert(#names == 1, "please specify only one addon name to be removed!")
+ local name = names[1]
+ local dir = _get_addondir(name)
+ assert(os.isdir(dir), "addon(%s) not found!", name)
+ os.rmdir(dir)
+ addon.unregister(name)
+ cprint("${color.success}remove ${bright}%s${clear} ok!", name)
+end
+
+-- get the description of an addon from its package description file
+function _addon_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("set_description%s*%(\"(.-)\"%)")
+ end
+ end
+end
+
+-- collect the installed addons from the addons registry
+--
+-- we always rescan the install directory here to repair the registry,
+-- e.g. the user may remove some addon directories manually
+--
+function _collect_installed_addons()
+ local entries = {}
+ for name, addoninfo in pairs(addon.rescan()) do
+ table.insert(entries, {name = name, version = addoninfo.version, payloads = addoninfo.payloads})
+ end
+ table.sort(entries, function (a, b) return a.name < b.name end)
+ return entries
+end
+
+-- collect the addons in the given repository, they follow the packages layout (addons/<first-letter>/<name>)
+function _collect_repo_addons(root, seen)
+ local entries = {}
+ for _, dir in ipairs(os.dirs(path.join(root, "*", "*"))) do
+ local name = path.filename(dir)
+ if os.isfile(path.join(dir, "xmake.lua")) and not seen[name] then
+ seen[name] = true
+ table.insert(entries, {name = name, description = _addon_description(dir)})
+ end
+ end
+ return entries
+end
+
+-- print an addon entry with its description aligned on the right
+function _print_addon(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 addons
+function _list()
+ local seen = {}
+ local installed = _collect_installed_addons()
+ for _, entry in ipairs(installed) do
+ seen[entry.name] = true
+ end
+ local avail = {}
+ for _, repo in ipairs(_repositories()) do
+ table.join2(avail, _collect_repo_addons(path.join(repo:directory(), "addons"), seen))
+ end
+
+ -- compute the alignment width from all addon names
+ local width = 0
+ for _, entries in ipairs({installed, avail}) do
+ for _, entry in ipairs(entries) do
+ width = math.max(width, #entry.name + 4)
+ end
+ end
+
+ -- installed addons
+ cprint("${bright}the installed addons:${clear}")
+ if #installed > 0 then
+ for _, entry in ipairs(installed) do
+ local note = string.format("(%s, %s)", entry.version, table.concat(entry.payloads, ", "))
+ _print_addon(entry.name, nil, width, note)
+ end
+ else
+ print(" (none)")
+ end
+
+ -- addons 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 addon --install %s to install)", entry.name)
+ _print_addon(entry.name, entry.description, width, note)
+ end
+ else
+ print(" (none)")
+ end
+end
+
+-- clear all installed addons
+function _clear()
+ addon.clear()
+ cprint("${color.success}clear all installed addons ok!")
+end
+
+function main()
+ if option.get("install") then
+ _install()
+ elseif option.get("remove") then
+ _remove()
+ elseif option.get("list") then
+ _list()
+ elseif option.get("clear") then
+ _clear()
+ end
+end