summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaikari <[email protected]>2026-07-10 22:28:38 +0300
committerruki <[email protected]>2026-07-30 10:17:31 +0800
commit4b6a2eb23d0903124871f837798db2dd718e86a1 (patch)
treec0dda814d7b199e1c54db61803c4051f4a834a3b
parenta928217681c4c5dc60b80a230182e5254bc30c8d (diff)
feat: add support for plugin packages in xmake
-rw-r--r--xmake/core/package/package.lua22
-rw-r--r--xmake/modules/package/manager/xmake/search_package.lua52
-rw-r--r--xmake/modules/private/action/require/impl/actions/install.lua12
-rw-r--r--xmake/modules/private/action/require/impl/package.lua1
-rw-r--r--xmake/modules/private/action/require/impl/remove_packages.lua6
-rw-r--r--xmake/modules/private/action/require/impl/repository.lua42
-rw-r--r--xmake/modules/private/action/require/impl/uninstall_packages.lua5
-rw-r--r--xmake/modules/private/action/require/impl/utils/plugins.lua51
-rw-r--r--xmake/modules/private/action/require/search.lua18
-rw-r--r--xmake/modules/private/check/checkers/api/package/kind.lua2
-rw-r--r--xmake/modules/private/xrepo/action/install.lua27
-rw-r--r--xmake/modules/private/xrepo/action/remove.lua13
-rw-r--r--xmake/modules/private/xrepo/action/search.lua15
-rw-r--r--xmake/modules/private/xrepo/quick_search/cache.lua14
-rw-r--r--xmake/plugins/plugin/main.lua75
-rw-r--r--xmake/plugins/plugin/xmake.lua10
-rw-r--r--xmake/plugins/xfetch/main.lua289
-rw-r--r--xmake/plugins/xfetch/xmake.lua32
18 files changed, 643 insertions, 43 deletions
diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua
index 0d164ce95..3537740e3 100644
--- a/xmake/core/package/package.lua
+++ b/xmake/core/package/package.lua
@@ -584,6 +584,8 @@ end
-- - binary
-- - toolchain (is also binary)
-- - library(default)
+-- - template
+-- - plugin
--
function _instance:kind()
local kind
@@ -623,6 +625,14 @@ 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?
--
-- @return true if the package kind is "headeronly"
@@ -750,7 +760,7 @@ function _instance:is_host()
if requireinfo and requireinfo.host then
return true
end
- return self:is_binary()
+ return self:is_binary() or self:is_plugin()
end
-- is cross-compilation?
@@ -2111,6 +2121,16 @@ 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 32991ae3e..04d69d135 100644
--- a/xmake/modules/package/manager/xmake/search_package.lua
+++ b/xmake/modules/package/manager/xmake/search_package.lua
@@ -27,31 +27,43 @@ function _search_package(packages, name, opt)
local packagename = packageinfo.name
local packagedata = packageinfo.data
- local version
- local versions = packagedata.versions
- if versions then
- versions = table.copy(versions)
- table.sort(versions, function (a, b) return semver.compare(a, b) > 0 end)
- if opt.require_version then
- for _, ver in ipairs(versions) do
- if semver.satisfies(ver, opt.require_version) then
- version = ver
+ -- only search the packages with the given kind, e.g. plugin,
+ -- and we will ignore the plugin packages by default
+ local kind_matched
+ if opt.kind then
+ kind_matched = packagedata.kind == opt.kind
+ else
+ kind_matched = packagedata.kind ~= "plugin"
+ end
+ if kind_matched then
+
+ local version
+ local versions = packagedata.versions
+ if versions then
+ versions = table.copy(versions)
+ table.sort(versions, function (a, b) return semver.compare(a, b) > 0 end)
+ if opt.require_version then
+ for _, ver in ipairs(versions) do
+ if semver.satisfies(ver, opt.require_version) then
+ version = ver
+ end
end
+ else
+ version = versions[1]
end
- else
- version = versions[1]
end
- end
- local description = packagedata.description
- if description then
- description = description:gsub(string.ipattern(name), function (w)
- return "${bright}" .. w .. "${clear}"
- end)
- end
+ local description = packagedata.description
+ -- do not highlight the description if the pattern starts with a quantifier, e.g. `xrepo search -k plugin "*"`
+ if description and not name:startswith("*") and not name:startswith("+") then
+ description = description:gsub(string.ipattern(name), function (w)
+ return "${bright}" .. w .. "${clear}"
+ end)
+ end
- if not opt.require_version or version then
- packages[packagename] = {name = packagename, version = version, description = description, reponame = packagedata.reponame}
+ if not opt.require_version or version then
+ packages[packagename] = {name = packagename, version = version, description = description, reponame = packagedata.reponame, kind = packagedata.kind}
+ end
end
end
end
diff --git a/xmake/modules/private/action/require/impl/actions/install.lua b/xmake/modules/private/action/require/impl/actions/install.lua
index 7fb54a951..3d1c15621 100644
--- a/xmake/modules/private/action/require/impl/actions/install.lua
+++ b/xmake/modules/private/action/require/impl/actions/install.lua
@@ -32,6 +32,7 @@ import("private.action.require.impl.actions.test")
import("private.action.require.impl.actions.patch_sources")
import("private.action.require.impl.actions.download_resources")
import("private.action.require.impl.utils.filter")
+import("private.action.require.impl.utils.plugins")
-- patch pkgconfig if not exists
function _patch_pkgconfig(package)
@@ -513,6 +514,12 @@ function main(package)
end
assert(fetchinfo, "fetch %s failed!", package_tipname)
+ -- register the plugin package to the global plugins directory,
+ -- we need to register it before testing, because the test script may run this plugin task.
+ if package:is_plugin() then
+ plugins.register(package)
+ end
+
-- this package is installed now
if installed_now then
@@ -557,6 +564,11 @@ function main(package)
-- leave the package environments
os.setenvs(oldenvs)
+ -- unregister the broken plugin package
+ if package:is_plugin() then
+ plugins.unregister(package:name())
+ end
+
-- copy the invalid package directory to cache
local installdir = package:installdir()
if os.isdir(installdir) then
diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua
index 3ad758c3a..a3b58a0bd 100644
--- a/xmake/modules/private/action/require/impl/package.lua
+++ b/xmake/modules/private/action/require/impl/package.lua
@@ -949,6 +949,7 @@ function _load_package(packagename, requireinfo, opt)
plat = requireinfo.plat,
arch = requireinfo.arch,
name = requireinfo.reponame,
+ kind = requireinfo.kind,
locked_repo = locked_requireinfo and locked_requireinfo.repo})
if package then
from_repo = true
diff --git a/xmake/modules/private/action/require/impl/remove_packages.lua b/xmake/modules/private/action/require/impl/remove_packages.lua
index a10fc803d..dfe50c3a4 100644
--- a/xmake/modules/private/action/require/impl/remove_packages.lua
+++ b/xmake/modules/private/action/require/impl/remove_packages.lua
@@ -23,6 +23,7 @@ import("core.base.option")
import("core.base.hashset")
import("core.package.package")
import("core.cache.localcache")
+import("private.action.require.impl.utils.plugins")
-- get package configs string
function _get_package_configs_str(manifest_file)
@@ -94,7 +95,12 @@ function _remove_packagedirs(packagedir, opt)
local description = string.format("remove ${color.dump.string}%s-%s${clear}/${yellow}%s${clear}\n -> ${dim}%s${clear} (${red}%s${clear})", package_name, version, hash, configs_str, status and status or "used")
local confirm = utils.confirm({default = true, description = description})
if confirm then
+ local manifest = os.isfile(manifest_file) and io.load(manifest_file)
os.rm(hashdir)
+ -- unregister the plugin package from the global plugins directory
+ if manifest and manifest.kind == "plugin" then
+ plugins.unregister(manifest.name or package_name)
+ end
end
end
end
diff --git a/xmake/modules/private/action/require/impl/repository.lua b/xmake/modules/private/action/require/impl/repository.lua
index 3df59c42c..e2ac794df 100644
--- a/xmake/modules/private/action/require/impl/repository.lua
+++ b/xmake/modules/private/action/require/impl/repository.lua
@@ -27,8 +27,36 @@ import("core.package.repository")
import("devel.git")
import("net.proxy")
+-- find the package directory in the given repository directory
+--
+-- the layout of repository:
+--
+-- packages/z/zlib/xmake.lua
+-- plugins/hello/xmake.lua
+-- plugins/h/hello/xmake.lua
+--
+function _find_packagedir(repodir, packagename, opt)
+ opt = opt or {}
+ local packagedirs = {path.join("packages", packagename:sub(1, 1), packagename)}
+ local plugindirs = {path.join("plugins", packagename),
+ path.join("plugins", packagename:sub(1, 1), packagename)}
+ local dirs
+ if opt.kind == "plugin" then
+ -- find it from the plugin directories first
+ dirs = table.join(plugindirs, packagedirs)
+ else
+ dirs = table.join(packagedirs, plugindirs)
+ end
+ for _, dir in ipairs(dirs) do
+ dir = path.join(repodir, dir)
+ if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then
+ return dir
+ end
+ end
+end
+
-- get package directory from the locked repository
-function _get_packagedir_from_locked_repo(packagename, locked_repo)
+function _get_packagedir_from_locked_repo(packagename, locked_repo, opt)
-- find global repository directory
local repo_global
@@ -101,8 +129,8 @@ function _get_packagedir_from_locked_repo(packagename, locked_repo)
-- find package directory
local foundir
if ok then
- local dir = path.join(repodir_local, "packages", packagename:sub(1, 1), packagename)
- if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) then
+ local dir = _find_packagedir(repodir_local, packagename, opt)
+ if dir then
local repo = repository.load(reponame, locked_repo.url, locked_repo.branch, false)
foundir = {dir, repo}
vprint("lock package(%s) in %s from repository(%s)/%s", packagename, dir, locked_repo.url, locked_repo.commit)
@@ -162,7 +190,7 @@ function packagedir(packagename, opt)
-- get cache key
local reponame = opt.name
- local cachekey = packagename
+ local cachekey = packagename .. (opt.kind or "")
local locked_repo = opt.locked_repo
if locked_repo then
cachekey = cachekey .. locked_repo.url .. (locked_repo.commit or "") .. (locked_repo.branch or "")
@@ -179,14 +207,14 @@ function packagedir(packagename, opt)
-- find the package directory from the locked repository
if locked_repo then
- foundir = _get_packagedir_from_locked_repo(packagename, locked_repo)
+ foundir = _get_packagedir_from_locked_repo(packagename, locked_repo, opt)
end
-- find the package directory from repositories
if not foundir then
for _, repo in ipairs(repositories()) do
- local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1), packagename)
- if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) and (not reponame or reponame == repo:name()) then
+ local dir = _find_packagedir(repo:directory(), packagename, opt)
+ if dir and (not reponame or reponame == repo:name()) then
foundir = {dir, repo}
break
end
diff --git a/xmake/modules/private/action/require/impl/uninstall_packages.lua b/xmake/modules/private/action/require/impl/uninstall_packages.lua
index 685b5c973..f5434fb17 100644
--- a/xmake/modules/private/action/require/impl/uninstall_packages.lua
+++ b/xmake/modules/private/action/require/impl/uninstall_packages.lua
@@ -21,6 +21,7 @@
-- imports
import("core.cache.localcache")
import("private.action.require.impl.package")
+import("private.action.require.impl.utils.plugins")
-- uninstall packages
-- uninstall required packages
@@ -46,6 +47,10 @@ function main(requires, opt)
table.insert(packages, instance)
end
os.tryrm(instance:installdir())
+ -- unregister the plugin package from the global plugins directory
+ if instance:is_plugin() then
+ plugins.unregister(instance:name())
+ end
end
return packages
end
diff --git a/xmake/modules/private/action/require/impl/utils/plugins.lua b/xmake/modules/private/action/require/impl/utils/plugins.lua
new file mode 100644
index 000000000..9cd7c5d86
--- /dev/null
+++ b/xmake/modules/private/action/require/impl/utils/plugins.lua
@@ -0,0 +1,51 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file plugins.lua
+--
+
+-- imports
+import("core.base.global")
+
+-- get the plugin directory of the given plugin package in the global plugins directory
+function plugindir(name)
+ return path.join(global.directory(), "plugins", name)
+end
+
+-- register the given plugin package to the global plugins directory,
+-- then we can run it directly. e.g. `xmake plugin-name`
+function register(package)
+ local installdir = package:installdir()
+ assert(os.isfile(path.join(installdir, "xmake.lua")),
+ "plugin(%s): xmake.lua not found in the installed files, it should be installed with `os.cp(\"*\", package:installdir())`!", package:name())
+ local dir = plugindir(package:name())
+ os.tryrm(dir)
+ os.cp(installdir, dir)
+ -- remove the install logs, they do not belong to the plugin,
+ -- but we keep manifest.txt to show the plugin version and description. e.g. `xmake plugin --list`
+ os.tryrm(path.join(dir, "logs"))
+ vprint("register plugin(%s) to %s", package:name(), dir)
+end
+
+-- unregister the given plugin from the global plugins directory
+function unregister(name)
+ local dir = plugindir(name)
+ if os.isdir(dir) then
+ os.tryrm(dir)
+ vprint("unregister plugin(%s) from %s", name, dir)
+ end
+end
diff --git a/xmake/modules/private/action/require/search.lua b/xmake/modules/private/action/require/search.lua
index e714c6c19..75ebd8d50 100644
--- a/xmake/modules/private/action/require/search.lua
+++ b/xmake/modules/private/action/require/search.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.task")
+import("core.base.option")
import("private.action.require.impl.utils.filter")
import("private.action.require.impl.repository")
import("private.action.require.impl.environment")
@@ -41,11 +42,22 @@ function main(names)
task.run("repo", {update = true})
end
+ -- get the extra search options, e.g. --extra="{kind='plugin'}"
+ local opt = {}
+ local extra = option.get("extra")
+ if extra then
+ local extrainfo, errors = string.deserialize(extra)
+ if not extrainfo then
+ raise(errors)
+ end
+ opt.kind = extrainfo.kind
+ end
+
-- show title
print("The package names:")
-- search packages
- for name, packages in pairs(search_packages(names)) do
+ for name, packages in pairs(search_packages(names, opt)) do
if #packages > 0 then
-- show name
@@ -57,8 +69,10 @@ function main(names)
local version = result.version
local reponame = result.reponame
local description = result.description
- cprint(" -> ${color.dump.string}%s%s${clear}: %s %s", name,
+ local kind = result.kind
+ cprint(" -> ${color.dump.string}%s%s${clear}%s: %s %s", name,
version and ("-" .. version) or "",
+ kind == "plugin" and " ${magenta}(plugin)${clear}" or "",
description or "",
reponame and ("(in " .. reponame .. ")") or "")
end
diff --git a/xmake/modules/private/check/checkers/api/package/kind.lua b/xmake/modules/private/check/checkers/api/package/kind.lua
index 4a9c309e3..d2c7afd10 100644
--- a/xmake/modules/private/check/checkers/api/package/kind.lua
+++ b/xmake/modules/private/check/checkers/api/package/kind.lua
@@ -37,6 +37,6 @@ function main(opt)
end
return true
end
- return value == "binary" or value == "toolchain" or value == "template"
+ return value == "binary" or value == "toolchain" or value == "template" or value == "plugin"
end}))
end
diff --git a/xmake/modules/private/xrepo/action/install.lua b/xmake/modules/private/xrepo/action/install.lua
index b0d297b38..6b38bca0f 100644
--- a/xmake/modules/private/xrepo/action/install.lua
+++ b/xmake/modules/private/xrepo/action/install.lua
@@ -30,8 +30,8 @@ function menu_options()
-- menu options
local options =
{
- {'k', "kind", "kv", nil, "Enable static/shared library.",
- values = {"static", "shared"} },
+ {'k', "kind", "kv", nil, "Enable static/shared library or install plugin package.",
+ values = {"static", "shared", "plugin"}},
{'p', "plat", "kv", nil, "Set the given platform." },
{'a', "arch", "kv", nil, "Set the given architecture." },
{'m', "mode", "kv", nil, "Set the given mode.",
@@ -47,6 +47,10 @@ function menu_options()
"e.g.",
" - xrepo install -p cross --toolchain=mytool --includes='toolchain1.lua" .. path.envsep() .. "toolchain2.lua'"},
{nil, "policies", "kv", nil, "Set the policies." },
+ {nil, "repo", "kv", nil, "Set the given repository.",
+ "e.g.",
+ " - xrepo install --repo=my-repo zlib",
+ " - xrepo install -k plugin --repo=my-repo hello-world"},
{category = "Visual Studio SDK Configuration" },
{nil, "vs", "kv", nil, "The Microsoft Visual Studio"
, " e.g. --vs=2017" },
@@ -93,6 +97,7 @@ function menu_options()
" - xrepo install -p android [--ndk=/xxx] -m debug \"pcre2 10.x\"",
" - xrepo install -p mingw [--mingw=/xxx] -k shared zlib",
" - xrepo install conan::zlib/1.2.11 vcpkg::zlib",
+ " - xrepo install -k plugin hello-world",
values = function (complete, opt) return import("private.xrepo.quick_search.completion")(complete, opt) end}
}
@@ -178,7 +183,7 @@ function _install_packages(packages)
table.insert(config_argv, mode)
end
local kind = option.get("kind")
- if kind then
+ if kind and kind ~= "plugin" then
table.insert(config_argv, "-k")
table.insert(config_argv, kind)
end
@@ -285,7 +290,9 @@ function _install_packages(packages)
if mode == "debug" then
extra.debug = true
end
- if kind then
+ if kind == "plugin" then
+ extra.kind = "plugin"
+ elseif kind then
extra.configs = extra.configs or {}
extra.configs.shared = kind == "shared"
end
@@ -306,6 +313,18 @@ function _install_packages(packages)
local extra_str = string.serialize(extra, {indent = false, strip = true})
table.insert(require_argv, "--extra=" .. extra_str)
end
+ -- install the packages from the given repository, e.g. xrepo install --repo=my-repo zlib
+ local repo = option.get("repo")
+ if repo then
+ local result = {}
+ for _, name in ipairs(packages) do
+ if not name:find("@", 1, true) and not name:find("::", 1, true) then
+ name = repo .. "@" .. name
+ end
+ table.insert(result, name)
+ end
+ packages = result
+ end
table.join2(require_argv, packages)
end
os.vexecv(os.programfile(), require_argv, {envs = envs})
diff --git a/xmake/modules/private/xrepo/action/remove.lua b/xmake/modules/private/xrepo/action/remove.lua
index ed92a83b8..b59f6b7b0 100644
--- a/xmake/modules/private/xrepo/action/remove.lua
+++ b/xmake/modules/private/xrepo/action/remove.lua
@@ -31,8 +31,8 @@ function menu_options()
-- menu options
local options =
{
- {'k', "kind", "kv", nil, "Enable static/shared library.",
- values = {"static", "shared"} },
+ {'k', "kind", "kv", nil, "Enable static/shared library or remove plugin package.",
+ values = {"static", "shared", "plugin"} },
{'p', "plat", "kv", nil, "Set the given platform." },
{'a', "arch", "kv", nil, "Set the given architecture." },
{'m', "mode", "kv", nil, "Set the given mode.",
@@ -56,7 +56,8 @@ function menu_options()
" - xrepo remove -p iphoneos -a arm64 \"zlib >=1.2.0\"",
" - xrepo remove -p android -m debug \"pcre2 10.x\"",
" - xrepo remove -p mingw -k shared zlib",
- " - xrepo remove conan::zlib/1.2.11 vcpkg::zlib" }
+ " - xrepo remove conan::zlib/1.2.11 vcpkg::zlib",
+ " - xrepo remove -k plugin hello-world" }
}
-- show menu options
@@ -137,7 +138,7 @@ function _remove_packages(packages)
table.insert(config_argv, mode)
end
local kind = option.get("kind")
- if kind then
+ if kind and kind ~= "plugin" then
table.insert(config_argv, "-k")
table.insert(config_argv, kind)
end
@@ -168,7 +169,9 @@ function _remove_packages(packages)
if mode == "debug" then
extra.debug = true
end
- if kind then
+ if kind == "plugin" then
+ extra.kind = "plugin"
+ elseif kind then
extra.configs = extra.configs or {}
extra.configs.shared = kind == "shared"
end
diff --git a/xmake/modules/private/xrepo/action/search.lua b/xmake/modules/private/xrepo/action/search.lua
index d389e3833..ff379c5db 100644
--- a/xmake/modules/private/xrepo/action/search.lua
+++ b/xmake/modules/private/xrepo/action/search.lua
@@ -30,10 +30,14 @@ function menu_options()
-- menu options
local options =
{
+ {'k', "kind", "kv", nil, "Set the package kind to be searched.",
+ values = {"plugin"} },
{nil, "packages", "vs", nil, "The packages list (support lua pattern).",
"e.g.",
" - xrepo search zlib boost",
- " - xrepo search \"pcre*\""}
+ " - xrepo search \"pcre*\"",
+ " - xrepo search -k plugin",
+ " - xrepo search -k plugin hello-world"}
}
-- show menu options
@@ -80,6 +84,11 @@ function _search_packages(packages)
if option.get("diagnosis") then
table.insert(require_argv, "-D")
end
+ local kind = option.get("kind")
+ if kind then
+ local extra_str = string.serialize({kind = kind}, {indent = false, strip = true})
+ table.insert(require_argv, "--extra=" .. extra_str)
+ end
table.join2(require_argv, packages)
os.vexecv(os.programfile(), require_argv)
end
@@ -87,6 +96,10 @@ end
-- main entry
function main()
local packages = option.get("packages")
+ if not packages and option.get("kind") then
+ -- list all packages with the given kind, e.g. xrepo search -k plugin
+ packages = {"*"}
+ end
if packages then
_search_packages(packages)
else
diff --git a/xmake/modules/private/xrepo/quick_search/cache.lua b/xmake/modules/private/xrepo/quick_search/cache.lua
index cf75a212d..ee0eacc67 100644
--- a/xmake/modules/private/xrepo/quick_search/cache.lua
+++ b/xmake/modules/private/xrepo/quick_search/cache.lua
@@ -42,6 +42,19 @@ function _list_package_dirs()
end
end
end
+ -- find the plugin package directories, e.g. plugins/hello/xmake.lua, plugins/h/hello/xmake.lua
+ for _, file in ipairs(table.join(os.files(path.join(repo:directory(), "plugins", "*", "xmake.lua")),
+ os.files(path.join(repo:directory(), "plugins", "*", "*", "xmake.lua")))) do
+ local dir = path.directory(file)
+ local subdirname = path.basename(path.directory(dir))
+ if subdirname == "plugins" or #subdirname == 1 then
+ local packagename = path.filename(dir)
+ if not unique[packagename] then
+ table.insert(packageinfos, {name = packagename, repo = repo, packagedir = dir})
+ unique[packagename] = true
+ end
+ end
+ end
end
return packageinfos
end
@@ -61,6 +74,7 @@ function update()
reponame = package:repo() and package:repo():name(),
description = package:description(),
versions = package:versions(),
+ kind = package:kind(),
})
end
cache:save()
diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua
index b362381e4..1ab4d1a9e 100644
--- a/xmake/plugins/plugin/main.lua
+++ b/xmake/plugins/plugin/main.lua
@@ -63,9 +63,43 @@ 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, [email protected]:xmake-io/xmake-plugins.git, /tmp/xmake-plugins
+function _is_package_name(str)
+ return not os.isdir(str) and not str:find("[/\\:]")
+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?
+ local name = option.get("plugins")
+ if name and _is_package_name(name) then
+ return _install_package(name)
+ end
+
-- enter environment
environment.enter()
@@ -118,6 +152,12 @@ 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()
@@ -158,6 +198,37 @@ function _update()
environment.leave()
end
+-- remove the given installed plugin
+function _remove()
+ local name = assert(option.get("plugins"), "please specify the plugin name to be removed!")
+ 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)
+end
+
+-- list all installed plugins
+function _list()
+ local plugindir = path.join(global.directory(), "plugins")
+ cprint("plugins in ${bright}%s${clear}:", plugindir)
+ for _, dir in ipairs(os.dirs(path.join(plugindir, "*"))) 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
+ end
+ end
+ cprint(" ${color.dump.string}%s${clear}%s: %s", path.filename(dir),
+ version and ("-" .. version) or "",
+ description or "")
+ end
+ end
+end
+
-- clear all installed plugins
function _clear()
local plugindir = path.join(global.directory(), "plugins")
@@ -172,6 +243,10 @@ function main()
_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
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"}
}
}
diff --git a/xmake/plugins/xfetch/main.lua b/xmake/plugins/xfetch/main.lua
new file mode 100644
index 000000000..357219486
--- /dev/null
+++ b/xmake/plugins/xfetch/main.lua
@@ -0,0 +1,289 @@
+--!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
new file mode 100644
index 000000000..a3296d233
--- /dev/null
+++ b/xmake/plugins/xfetch/xmake.lua
@@ -0,0 +1,32 @@
+--!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"}
+ }
+ }