diff options
| author | Saikari <[email protected]> | 2026-07-10 22:28:38 +0300 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-07-30 10:17:31 +0800 |
| commit | 4b6a2eb23d0903124871f837798db2dd718e86a1 (patch) | |
| tree | c0dda814d7b199e1c54db61803c4051f4a834a3b | |
| parent | a928217681c4c5dc60b80a230182e5254bc30c8d (diff) | |
feat: add support for plugin packages in xmake
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"} + } + } |
