From 8564ea66201d1398cb8ee5b91efc784a379c4a64 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 8 Aug 2026 22:41:30 +0800 Subject: fix plugin bug and add description --- tests/actions/addon/test.lua | 167 +++++++++++++++++++++ tests/plugins/repository/test.lua | 118 --------------- xmake/actions/addon/main.lua | 5 +- xmake/core/package/addon.lua | 19 ++- xmake/core/package/package.lua | 9 +- .../action/require/impl/actions/install.lua | 3 +- 6 files changed, 193 insertions(+), 128 deletions(-) create mode 100644 tests/actions/addon/test.lua delete mode 100644 tests/plugins/repository/test.lua diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua new file mode 100644 index 000000000..17a5dc33c --- /dev/null +++ b/tests/actions/addon/test.lua @@ -0,0 +1,167 @@ +import("core.base.global") + +-- write a minimal plugin that prints its name when run +-- +-- the plugins of an addon are placed in its `plugins` payload directory, +-- e.g. /plugins//xmake.lua +function _write_plugin(dir, name) + io.writefile(path.join(dir, "xmake.lua"), string.format([[ +task("%s") + set_category("plugin") + on_run("main") + set_menu {usage = "xmake %s", description = "say hello from %s"} +]], name, name, name)) + io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], name)) +end + +-- write a minimal template into the `templates` payload directory of an addon, +-- e.g. /templates///xmake.lua +function _write_template(dir, lang, templateid) + local templatedir = path.join(dir, "templates", lang, templateid) + io.writefile(path.join(templatedir, "xmake.lua"), [[ +target("${TARGET_NAME}") + set_kind("binary") + add_files("src/*.c") +]]) + io.writefile(path.join(templatedir, "src", "main.c"), [[ +int main(int argc, char** argv) { return 0; } +]]) +end + +-- write an addon payload directory, it provides a plugin and a template +function _write_addon(dir, name) + _write_plugin(path.join(dir, "plugins", name), name) + _write_template(dir, "c", name) +end + +-- write an addon package description, its payloads are placed in the `src` directory +-- +-- addons in a repository are described as packages, e.g. /addons///xmake.lua +function _write_addon_package(dir, name) + io.writefile(path.join(dir, "xmake.lua"), string.format([[ +package("%s") + set_kind("addon") + set_description("say hello from %s") + set_sourcedir(path.join(os.scriptdir(), "src")) +]], name, name)) + _write_addon(path.join(dir, "src"), name) +end + +-- create a temporary addon repository (packages layout: addons//) and register it +-- +-- @return reponame, names, cleanup +function _mock_repo(basenames) + local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") + local reponame = "addon-test-repo-" .. suffix + local repodir = os.tmpfile() .. ".addon-repo" + local names = {} + for _, base in ipairs(basenames) do + local name = base .. "-" .. suffix + _write_addon_package(path.join(repodir, "addons", name:sub(1, 1), name), name) + table.insert(names, name) + end + + -- register the repository into the cache + local cachefile = path.join(global.cachedir(), "repository") + local cache = os.isfile(cachefile) and io.load(cachefile) or {} + cache.repositories = cache.repositories or {} + cache.repositories[reponame] = {repodir} + io.save(cachefile, cache) + + local function cleanup() + for _, name in ipairs(names) do + os.tryrm(path.join(global.directory(), "addons", name)) + try { function () os.runv("xmake", {"addon", "--remove", name}) end } + end + local cache = os.isfile(cachefile) and io.load(cachefile) or {} + if cache.repositories then + cache.repositories[reponame] = nil + end + io.save(cachefile, cache) + os.tryrm(repodir) + end + return reponame, names, cleanup +end + +-- install an addon from a repository, by plain name and by repo@name +function test_install_from_repo(t) + local reponame, names, cleanup = _mock_repo({"hello"}) + local name = names[1] + + -- install by plain name (searched across all repositories) + os.runv("xmake", {"addon", "--install", "-y", name}) + t:require(os.iorunv("xmake", {name}):find(name, 1, true)) + + -- reinstall by repo@name + os.runv("xmake", {"addon", "--remove", name}) + os.runv("xmake", {"addon", "--install", "-y", reponame .. "@" .. name}) + t:require(os.iorunv("xmake", {name}):find(name, 1, true)) + + os.runv("xmake", {"addon", "--remove", name}) + cleanup() +end + +-- the templates of an installed addon can be used by `xmake create` +function test_install_templates(t) + local _, names, cleanup = _mock_repo({"hello"}) + local name = names[1] + os.runv("xmake", {"addon", "--install", "-y", name}) + + -- this template should be listed and grouped by its addon name + local out = os.iorunv("xmake", {"create", "--list"}) + t:require(out:find(name, 1, true)) + + -- we can create a new project from it + local projectdir = os.tmpfile() .. ".addon-project" + os.tryrm(projectdir) + os.runv("xmake", {"create", "-l", "c", "-t", name, "-P", projectdir}) + t:require(os.isfile(path.join(projectdir, "xmake.lua"))) + t:require(os.isfile(path.join(projectdir, "src", "main.c"))) + + os.tryrm(projectdir) + os.runv("xmake", {"addon", "--remove", name}) + cleanup() +end + +-- install an addon from a local directory, then remove it +function test_install_from_local(t) + local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") + local name = "hello-local-" .. suffix + local dir = path.join(os.tmpfile() .. ".addon-local", name) + _write_addon(dir, name) + + os.runv("xmake", {"addon", "--install", dir}) + t:require(os.iorunv("xmake", {name}):find(name, 1, true)) + + -- the removed addon should no longer be runnable + os.runv("xmake", {"addon", "--remove", name}) + t:require_not(try { function () os.runv("xmake", {name}); return true end }) + + os.tryrm(path.directory(dir)) +end + +-- --list shows the installed and available addons +function test_list(t) + local _, names, cleanup = _mock_repo({"hello", "world"}) + + -- install the first addon, leave the second only available + os.runv("xmake", {"addon", "--install", "-y", names[1]}) + local out = os.iorunv("xmake", {"addon", "--list"}) + t:require(out:find("the installed addons:", 1, true)) + t:require(out:find(names[1], 1, true)) + t:require(out:find(names[2], 1, true)) + t:require(out:find("xmake addon --install " .. names[2], 1, true)) + + -- the payloads of the installed addon should be shown, e.g. (latest, plugins, templates) + t:require(out:find("plugins", 1, true)) + t:require(out:find("templates", 1, true)) + + os.runv("xmake", {"addon", "--remove", names[1]}) + cleanup() +end + +-- invalid installs should fail +function test_install_invalid(t) + t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "addon-test-missing"}); return true end }) + t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "somerepo@.."}); return true end }) +end diff --git a/tests/plugins/repository/test.lua b/tests/plugins/repository/test.lua deleted file mode 100644 index 7b1619d5e..000000000 --- a/tests/plugins/repository/test.lua +++ /dev/null @@ -1,118 +0,0 @@ -import("core.base.global") - --- write a minimal plugin that prints its name when run -function _write_plugin(dir, name) - io.writefile(path.join(dir, "xmake.lua"), string.format([[ -task("%s") - set_category("plugin") - on_run("main") - set_menu {usage = "xmake %s", description = "say hello from %s"} -]], name, name, name)) - io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], name)) -end - --- write a plugin package description, the plugin sources are placed in its `src` directory --- --- plugins in a repository are described as packages, e.g. /plugins///xmake.lua -function _write_plugin_package(dir, name) - io.writefile(path.join(dir, "xmake.lua"), string.format([[ -package("%s") - set_kind("plugin") - set_description("say hello from %s") - set_sourcedir(path.join(os.scriptdir(), "src")) -]], name, name)) - _write_plugin(path.join(dir, "src"), name) -end - --- create a temporary plugin repository (packages layout: plugins//) and register it --- --- @return reponame, names, cleanup -function _mock_repo(basenames) - local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") - local reponame = "plugin-test-repo-" .. suffix - local repodir = os.tmpfile() .. ".plugin-repo" - local names = {} - for _, base in ipairs(basenames) do - local name = base .. "-" .. suffix - _write_plugin_package(path.join(repodir, "plugins", name:sub(1, 1), name), name) - table.insert(names, name) - end - - -- register the repository into the cache - local cachefile = path.join(global.cachedir(), "repository") - local cache = os.isfile(cachefile) and io.load(cachefile) or {} - cache.repositories = cache.repositories or {} - cache.repositories[reponame] = {repodir} - io.save(cachefile, cache) - - local function cleanup() - for _, name in ipairs(names) do - os.tryrm(path.join(global.directory(), "plugins", name)) - end - local cache = os.isfile(cachefile) and io.load(cachefile) or {} - if cache.repositories then - cache.repositories[reponame] = nil - end - io.save(cachefile, cache) - os.tryrm(repodir) - end - return reponame, names, cleanup -end - --- install a plugin from a repository, by plain name and by repo@name -function test_install_from_repo(t) - local reponame, names, cleanup = _mock_repo({"hello"}) - local name = names[1] - - -- install by plain name (searched across all repositories) - os.runv("xmake", {"plugin", "--install", "-y", name}) - t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - - -- reinstall by repo@name - os.runv("xmake", {"plugin", "--remove", name}) - os.runv("xmake", {"plugin", "--install", "-y", reponame .. "@" .. name}) - t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - - os.runv("xmake", {"plugin", "--remove", name}) - cleanup() -end - --- install a plugin from a local directory, then remove it -function test_install_from_local(t) - local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "") - local name = "hello-local-" .. suffix - local dir = path.join(os.tmpfile() .. ".plugin-local", name) - _write_plugin(dir, name) - - os.runv("xmake", {"plugin", "--install", dir}) - t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - - -- the removed plugin should no longer be runnable - os.runv("xmake", {"plugin", "--remove", name}) - t:require_not(try { function () os.runv("xmake", {name}); return true end }) - - os.tryrm(path.directory(dir)) -end - --- --list shows the built-in, installed and available plugins -function test_list(t) - local reponame, names, cleanup = _mock_repo({"hello", "world"}) - - -- install the first plugin, leave the second only available - os.runv("xmake", {"plugin", "--install", "-y", names[1]}) - local out = os.iorunv("xmake", {"plugin", "--list"}) - t:require(out:find("the built-in plugins:", 1, true)) - t:require(out:find("project", 1, true)) - t:require(out:find(names[1], 1, true)) - t:require(out:find(names[2], 1, true)) - t:require(out:find("xmake plugin --install " .. names[2], 1, true)) - - os.runv("xmake", {"plugin", "--remove", names[1]}) - cleanup() -end - --- invalid installs should fail -function test_install_invalid(t) - t:require_not(try { function () os.runv("xmake", {"plugin", "--install", "-y", "plugin-test-missing"}); return true end }) - t:require_not(try { function () os.runv("xmake", {"plugin", "--install", "-y", "somerepo@.."}); return true end }) -end diff --git a/xmake/actions/addon/main.lua b/xmake/actions/addon/main.lua index d072cec7f..52cb2376b 100644 --- a/xmake/actions/addon/main.lua +++ b/xmake/actions/addon/main.lua @@ -179,7 +179,8 @@ end 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}) + table.insert(entries, {name = name, version = addoninfo.version, + description = addoninfo.description, payloads = addoninfo.payloads}) end table.sort(entries, function (a, b) return a.name < b.name end) return entries @@ -237,7 +238,7 @@ function _list() 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) + _print_addon(entry.name, entry.description, width, note) end else print(" (none)") diff --git a/xmake/core/package/addon.lua b/xmake/core/package/addon.lua index 35966853d..d433bdf8a 100644 --- a/xmake/core/package/addon.lua +++ b/xmake/core/package/addon.lua @@ -170,11 +170,15 @@ end -- -- @param name the addon name -- @param version the addon version, e.g. "1.0.1", "latest" +-- @param opt the options, e.g. {description = "..."} -- -function addon.register(name, version) +function addon.register(name, version, opt) + opt = opt or {} local dirname = addon.dirname(name) local addons = addon.addons() - addons[dirname] = {version = version, payloads = addon.payloads_of(path.join(addon.installdir(), dirname, version))} + addons[dirname] = {version = version, + description = opt.description, + payloads = addon.payloads_of(path.join(addon.installdir(), dirname, version))} addon._save(addons) end @@ -193,11 +197,20 @@ end -- it's only used to repair the registry file, e.g. the user removed some addon directories manually -- function addon.rescan() + local oldaddons = addon.addons() 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} + local dirname = path.filename(path.directory(versiondir)) + local version = path.filename(versiondir) + -- we need to keep the description, we cannot get it from the installed payloads + local oldaddoninfo = oldaddons[dirname] + local description + if oldaddoninfo and oldaddoninfo.version == version then + description = oldaddoninfo.description + end + addons[dirname] = {version = version, description = description, payloads = payloads} end end addon._save(addons) diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index 2b56ee332..9d0cdc49d 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -1216,10 +1216,11 @@ function _instance:_rawenvs() end -- add plugin env for on_test - if self:is_addon() then - -- e.g. ~/.xmake/addons///plugins - envs.XMAKE_PLUGIN_DIRS = path.join(self:installdir(), "plugins") - elseif self:is_plugin() then + -- + -- @note we need not do it for the addon packages, they are registered + -- to the addons registry after installing, and xmake can find their payloads directly + -- + if self:is_plugin() then envs.XMAKE_PLUGIN_DIRS = path.directory(self:installdir()) end self._RAWENVS = envs diff --git a/xmake/modules/private/action/require/impl/actions/install.lua b/xmake/modules/private/action/require/impl/actions/install.lua index 7e60b16b6..40ca225f0 100644 --- a/xmake/modules/private/action/require/impl/actions/install.lua +++ b/xmake/modules/private/action/require/impl/actions/install.lua @@ -503,7 +503,8 @@ function main(package) -- register this addon, so that xmake can find its payloads, e.g. plugins if package:is_addon() then - addon.register(package:name(), package:version_str() or "latest") + addon.register(package:name(), package:version_str() or "latest", + {description = package:description()}) end installed_now = true end -- cgit v1.3.1