diff options
| author | ruki <[email protected]> | 2026-08-08 18:31:55 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-08-09 18:25:42 +0800 |
| commit | 53075cd271268c1931a41648a92891c362f2483a (patch) | |
| tree | 663735ec08e713b76fc0f8994d4f1eed80e36152 /xmake/core/package | |
| parent | 73a790f3c4810755921350c61cf84d6504a30297 (diff) | |
add addon module
Diffstat (limited to 'xmake/core/package')
| -rw-r--r-- | xmake/core/package/addon.lua | 200 | ||||
| -rw-r--r-- | xmake/core/package/package.lua | 35 |
2 files changed, 204 insertions, 31 deletions
diff --git a/xmake/core/package/addon.lua b/xmake/core/package/addon.lua new file mode 100644 index 000000000..e40a83a47 --- /dev/null +++ b/xmake/core/package/addon.lua @@ -0,0 +1,200 @@ +--!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 addon.lua +-- + +-- define module +local addon = addon or {} + +-- load modules +local os = require("base/os") +local io = require("base/io") +local path = require("base/path") +local table = require("base/table") +local utils = require("base/utils") +local global = require("base/global") + +-- the payload directories of an addon +-- +-- an addon can provide any subset of them, e.g. only `plugins` +-- +-- @note only `plugins` is activated for now, the others are reserved +-- +function addon.payloaddirs() + return {"plugins", "rules", "toolchains", "platforms", "modules", "templates", "themes", "includes"} +end + +-- the install directory of addons, e.g. ~/.xmake/addons +function addon.installdir() + return path.join(global.directory(), "addons") +end + +-- get the directory name of the given addon name, e.g. "myns::foo" -> "myns_foo" +function addon.dirname(name) + return (name:lower():gsub("::", "_")) +end + +-- the registry file of the installed addons, e.g. ~/.xmake/addons/addons.conf +-- +-- we save all installed addons to this file when installing/removing them, +-- so we do not need to scan the whole addons directory on startup +-- +function addon.registryfile() + return path.join(addon.installdir(), "addons.conf") +end + +-- get all installed addons +-- +-- @return the addons table, e.g. {["hello-world"] = {version = "latest", payloads = {"plugins"}}} +-- +function addon.addons() + local addons = addon._ADDONS + if addons == nil then + addons = {} + local registryfile = addon.registryfile() + if os.isfile(registryfile) then + addons = io.load(registryfile) or {} + end + addon._ADDONS = addons + end + return addons +end + +-- get the install directory of the given addon, e.g. ~/.xmake/addons/<name>/<version> +function addon.addondir(name, version) + local dirname = addon.dirname(name) + if version == nil then + local addoninfo = addon.addons()[dirname] + if addoninfo == nil then + return nil + end + version = addoninfo.version + end + return path.join(addon.installdir(), dirname, version) +end + +-- get the payload directories of the given kind from all installed addons +-- +-- @param kind the payload kind, e.g. "plugins", "rules" +-- @return the directories, e.g. {"~/.xmake/addons/hello-world/latest/plugins"} +-- +-- @note we do not check if these directories exist, the callers will just ignore the invalid ones +-- +function addon.payloads(kind) + local payloads = {} + for name, addoninfo in pairs(addon.addons()) do + if table.contains(addoninfo.payloads or {}, kind) then + table.insert(payloads, path.join(addon.installdir(), name, addoninfo.version, kind)) + end + end + return payloads +end + +-- get the payload directories of the given addon directory, e.g. {"plugins", "rules"} +function addon.payloads_of(addondir) + local payloads = {} + for _, payloaddir in ipairs(addon.payloaddirs()) do + if os.isdir(path.join(addondir, payloaddir)) then + table.insert(payloads, payloaddir) + end + end + return payloads +end + +-- get the default on_install script of addon packages +-- +-- we only install the payload directories of this addon, e.g. plugins, rules, toolchains, ... +-- +function addon.installscript() + return function (package) + local installed = false + for _, payloaddir in ipairs(addon.payloaddirs()) do + if os.isdir(payloaddir) then + os.cp(payloaddir, package:installdir()) + installed = true + end + end + if not installed then + os.raise("addon(%s): no payload directory found, e.g. plugins!", package:name()) + end + end +end + +-- save the given addons to the registry file +function addon._save(addons) + addon._ADDONS = addons + local registryfile = addon.registryfile() + -- we need not create an empty registry file if no addons are installed + if next(addons) == nil and not os.isfile(registryfile) then + return + end + local ok, errors = io.save(registryfile, addons) + if not ok then + utils.warning(errors) + end +end + +-- register the given installed addon +-- +-- @param name the addon name +-- @param version the addon version, e.g. "1.0.1", "latest" +-- +function addon.register(name, version) + local dirname = addon.dirname(name) + local addons = addon.addons() + addons[dirname] = {version = version, payloads = addon.payloads_of(path.join(addon.installdir(), dirname, version))} + addon._save(addons) +end + +-- unregister the given addon +function addon.unregister(name) + local dirname = addon.dirname(name) + local addons = addon.addons() + if addons[dirname] then + addons[dirname] = nil + addon._save(addons) + end +end + +-- rescan the install directory and rebuild the registry +-- +-- it's only used to repair the registry file, e.g. the user removed some addon directories manually +-- +function addon.rescan() + local addons = {} + for _, versiondir in ipairs(os.dirs(path.join(addon.installdir(), "*", "*"))) do + local payloads = addon.payloads_of(versiondir) + if #payloads > 0 then + addons[path.filename(path.directory(versiondir))] = {version = path.filename(versiondir), payloads = payloads} + end + end + addon._save(addons) + return addons +end + +-- clear all installed addons +function addon.clear() + local installdir = addon.installdir() + if os.isdir(installdir) then + os.rmdir(installdir) + end + addon._ADDONS = {} +end + +-- return module +return addon diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index 8f035b6b5..2b56ee332 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -37,6 +37,7 @@ local interpreter = require("base/interpreter") local select_script = require("base/private/select_script") local is_cross = require("base/private/is_cross") local memcache = require("cache/memcache") +local addon = require("package/addon") local toolchain = require("tool/toolchain") local compiler = require("tool/compiler") local linker = require("tool/linker") @@ -940,7 +941,7 @@ function _instance:installdir(...) if os.is_host("windows") then version_str = version_str:gsub("[>=<|%*]", "") end - installdir = path.join(package.addon_installdir(), name, version_str) + installdir = addon.addondir(self:name(), version_str) elseif self:is_plugin() then -- deprecated, @see the `addon` kind installdir = path.join(global.directory(), "plugins", name) @@ -3053,21 +3054,6 @@ function package.installdir(opt) return installdir end --- the install directory for addon packages, e.g. ~/.xmake/addons -function package.addon_installdir() - return path.join(global.directory(), "addons") -end - --- the payload directories of addon packages --- --- an addon can provide any subset of them, e.g. only `plugins` --- --- @note only `plugins` is activated for now, the others are reserved --- -function package.addon_payloaddirs() - return {"plugins", "rules", "toolchains", "platforms", "modules", "templates", "themes", "includes"} -end - -- the search directories function package.searchdirs() local searchdirs = global.get("pkg_searchdirs") @@ -3264,22 +3250,9 @@ function package.load_from_repository(packagename, packagedir, opt) return nil, string.format("%s: package(%s) not found!", scriptpath, packagename) end - -- we need set the default on_install script if it's addon package, - -- we only install the payload directories of this addon, e.g. plugins, rules, toolchains, ... + -- we need set the default on_install script if it's addon package if packageinfo:get("kind") == "addon" and not packageinfo:get("install") then - local on_install = function (pkg) - local installed = false - for _, payloaddir in ipairs(package.addon_payloaddirs()) do - if os.isdir(payloaddir) then - os.cp(payloaddir, pkg:installdir()) - installed = true - end - end - if not installed then - os.raise("addon(%s): no payload directory found, e.g. plugins!", pkg:name()) - end - end - packageinfo:set("install", on_install) + packageinfo:set("install", addon.installscript()) end -- we need set the default on_install script if it's plugin package |
