diff options
| author | ruki <[email protected]> | 2026-08-14 23:28:20 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-08-14 23:28:20 +0800 |
| commit | a79cf74f920c088528d66a67a59b3d8eaa1d4f7a (patch) | |
| tree | dca4cb0ca1fd210e04e8c59ffd9ab8c410747c1f | |
| parent | 6c246c12a09524780e5bfdc53bc27a892e4793a9 (diff) | |
autofetch addons
| -rw-r--r-- | tests/actions/addon/projects/autofetch-badinclude/xmake-addons.lua | 2 | ||||
| -rw-r--r-- | tests/actions/addon/projects/autofetch-badinclude/xmake.lua | 2 | ||||
| -rw-r--r-- | tests/actions/addon/projects/autofetch-badname/xmake-addons.lua | 2 | ||||
| -rw-r--r-- | tests/actions/addon/projects/autofetch-badname/xmake.lua | 2 | ||||
| -rw-r--r-- | tests/actions/addon/projects/autofetch/xmake-addons.lua | 1 | ||||
| -rw-r--r-- | tests/actions/addon/projects/autofetch/xmake.lua | 5 | ||||
| -rw-r--r-- | tests/actions/addon/test.lua | 58 | ||||
| -rw-r--r-- | xmake/core/base/interpreter.lua | 38 | ||||
| -rw-r--r-- | xmake/core/base/task.lua | 6 | ||||
| -rw-r--r-- | xmake/core/package/addon.lua | 11 | ||||
| -rw-r--r-- | xmake/core/project/addons.lua | 188 | ||||
| -rw-r--r-- | xmake/core/project/project.lua | 73 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/project/addons.lua | 48 | ||||
| -rw-r--r-- | xmake/modules/private/action/require/impl/install_packages.lua | 2 | ||||
| -rw-r--r-- | xmake/modules/private/action/require/impl/package.lua | 3 | ||||
| -rw-r--r-- | xmake/modules/private/addons/main.lua | 92 |
16 files changed, 532 insertions, 1 deletions
diff --git a/tests/actions/addon/projects/autofetch-badinclude/xmake-addons.lua b/tests/actions/addon/projects/autofetch-badinclude/xmake-addons.lua new file mode 100644 index 000000000..6931855ae --- /dev/null +++ b/tests/actions/addon/projects/autofetch-badinclude/xmake-addons.lua @@ -0,0 +1,2 @@ +-- this file only declares the addons, it cannot reference them +includes("@addon/custom-include/check") diff --git a/tests/actions/addon/projects/autofetch-badinclude/xmake.lua b/tests/actions/addon/projects/autofetch-badinclude/xmake.lua new file mode 100644 index 000000000..0d94cf61c --- /dev/null +++ b/tests/actions/addon/projects/autofetch-badinclude/xmake.lua @@ -0,0 +1,2 @@ +target("test") + set_kind("phony") diff --git a/tests/actions/addon/projects/autofetch-badname/xmake-addons.lua b/tests/actions/addon/projects/autofetch-badname/xmake-addons.lua new file mode 100644 index 000000000..165325fb7 --- /dev/null +++ b/tests/actions/addon/projects/autofetch-badname/xmake-addons.lua @@ -0,0 +1,2 @@ +-- the `addon` name is reserved for the addon references +add_addons("addon") diff --git a/tests/actions/addon/projects/autofetch-badname/xmake.lua b/tests/actions/addon/projects/autofetch-badname/xmake.lua new file mode 100644 index 000000000..0d94cf61c --- /dev/null +++ b/tests/actions/addon/projects/autofetch-badname/xmake.lua @@ -0,0 +1,2 @@ +target("test") + set_kind("phony") diff --git a/tests/actions/addon/projects/autofetch/xmake-addons.lua b/tests/actions/addon/projects/autofetch/xmake-addons.lua new file mode 100644 index 000000000..a85e0fe11 --- /dev/null +++ b/tests/actions/addon/projects/autofetch/xmake-addons.lua @@ -0,0 +1 @@ +add_addons("custom-include") diff --git a/tests/actions/addon/projects/autofetch/xmake.lua b/tests/actions/addon/projects/autofetch/xmake.lua new file mode 100644 index 000000000..4eaca0785 --- /dev/null +++ b/tests/actions/addon/projects/autofetch/xmake.lua @@ -0,0 +1,5 @@ +-- the addon is installed automatically, so we can use its includes file here +includes("@addon/custom-include/check") + +target("test") + set_kind("phony") diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua index 3542c907b..4db9c0ddf 100644 --- a/tests/actions/addon/test.lua +++ b/tests/actions/addon/test.lua @@ -114,6 +114,32 @@ function _config_project(content) return _run_project(content, {"config", "-y"}) end +-- copy the given fixture project to a temporary directory and run the given function in it +-- +-- @note we cannot run them in place, they would generate the lock and the build files, +-- @see tests/actions/addon/projects +-- +function _with_project(name, func) + local projectdir = os.tmpfile() .. ".addon-project" + os.tryrm(projectdir) + os.mkdir(projectdir) + os.cp(path.join(os.scriptdir(), "projects", name, "*"), projectdir) + local oldir = os.cd(projectdir) + try + { + function () + func(projectdir) + end, + finally + { + function () + os.cd(oldir) + os.tryrm(projectdir) + end + } + } +end + -- only the payloads should be installed, our own files should not function test_install(t) _with_addons({"custom-toolchain"}, function () @@ -223,6 +249,38 @@ target("test") end) end +-- the addons which a project declares in `xmake-addons.lua` are installed automatically +-- +-- @note they must be installed before loading the project, it may use their includes files +function test_autofetch(t) + local recipes = {["custom-include"] = ("set_sourcedir(%q)"):format(_addondir("custom-include"))} + _with_repo(recipes, function () + _remove("custom-include") + _with_project("autofetch", function (projectdir) + + -- it should be installed when loading the project, so that its includes file can be found + t:require(os.iorunv("xmake", {"config", "-y"}):find("custom-include: includes check is loaded", 1, true)) + + -- and it should be locked + local lockfile = path.join(projectdir, "xmake-addons.lock") + t:require(os.isfile(lockfile)) + t:require(io.load(lockfile)["custom-include"] ~= nil) + + -- we should not install it again + t:require_not(os.iorunv("xmake", {"config", "-y"}):find("install custom-include", 1, true)) + end) + end) +end + +-- the addons file only declares the addons, it cannot reference them +function test_autofetch_invalid(t) + for _, name in ipairs({"autofetch-badinclude", "autofetch-badname"}) do + _with_project(name, function () + t:require_not(try { function () os.runv("xmake", {"config", "-y"}); return true end }) + end) + end +end + -- the addons can be installed from a repository, by plain name and by repo@name function test_install_from_repo(t) local recipes = {["custom-plugin"] = ("set_sourcedir(%q)"):format(_addondir("custom-plugin"))} diff --git a/xmake/core/base/interpreter.lua b/xmake/core/base/interpreter.lua index ede772de0..01b689fe6 100644 --- a/xmake/core/base/interpreter.lua +++ b/xmake/core/base/interpreter.lua @@ -884,6 +884,36 @@ end -- -- the root api will affect these scopes -- +-- get the root file name of the included directories, e.g. includes("subdir") -> subdir/xmake.lua +function interpreter:includes_rootfilename() + return self._PRIVATE._INCLUDES_ROOTFILENAME or "xmake.lua" +end + +-- set the root file name of the included directories +-- +-- e.g. interp:includes_rootfilename_set("xmake-addons.lua") -> includes("subdir") -> subdir/xmake-addons.lua +-- +function interpreter:includes_rootfilename_set(filename) + self._PRIVATE._INCLUDES_ROOTFILENAME = filename +end + +-- can we include the referenced files? e.g. includes("@builtin/check"), includes("@addon/esp32/board") +function interpreter:includes_references() + return self._PRIVATE._INCLUDES_REFERENCES ~= false +end + +-- enable/disable the referenced files of includes() +-- +-- @param enabled enable them or not +-- @param hint the extra hint of the error message +-- +-- @note the addons file is loaded before the addons are installed, so it cannot reference them +-- +function interpreter:includes_references_set(enabled, hint) + self._PRIVATE._INCLUDES_REFERENCES = enabled + self._PRIVATE._INCLUDES_REFERENCES_HINT = hint +end + function interpreter:rootscope_set(scope_kind) assert(self and self._PRIVATE) self._PRIVATE._ROOTSCOPE = scope_kind @@ -1826,6 +1856,12 @@ function interpreter:api_builtin_includes(...) local subpaths_matched = {} for _, subpath in ipairs(subpaths) do local found = false + -- the referenced files are not always available, e.g. the addons file + if subpath:startswith("@") and not self:includes_references() then + local hint = self._PRIVATE._INCLUDES_REFERENCES_HINT + os.raise("includes(%s): the referenced files are not supported in %s!%s", + subpath, path.filename(curfile), hint and ("\n" .. hint) or "") + end -- attempt to find files from programdir/includes/*.lua -- e.g. includes("@builtin/check") if subpath:startswith("@builtin/") then @@ -1848,7 +1884,7 @@ function interpreter:api_builtin_includes(...) files = os.files(subpath) else -- @see https://github.com/xmake-io/xmake/issues/6026 - files = os.files(path.join(subpath, "xmake.lua")) + files = os.files(path.join(subpath, self:includes_rootfilename())) end if files and #files > 0 then table.join2(subpaths_matched, files) diff --git a/xmake/core/base/task.lua b/xmake/core/base/task.lua index a901490eb..2cd1fa58a 100644 --- a/xmake/core/base/task.lua +++ b/xmake/core/base/task.lua @@ -426,6 +426,12 @@ function task._is_conflicting(taskname, taskfile, filepath) return true end +-- clear the loaded tasks, e.g. some addons may be installed just now +function task.clear() + task._TASKS = nil + task._DIRECTORIES = nil +end + -- get all registered tasks -- -- @return the tasks table {name = task, ...} diff --git a/xmake/core/package/addon.lua b/xmake/core/package/addon.lua index 586a3ea15..6d56205fd 100644 --- a/xmake/core/package/addon.lua +++ b/xmake/core/package/addon.lua @@ -656,6 +656,17 @@ function addon.remove(name, opt) return true end +-- reload the addons registry and the caches which are built from it +-- +-- @note we need it if the addons have been installed by another process, +-- e.g. the addons which a project declares, @see core/project/project.lua +-- +function addon.reload() + addon._ADDONS = nil + addon._MANIFESTS = nil + addon._GLOBALMODULES = nil +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 diff --git a/xmake/core/project/addons.lua b/xmake/core/project/addons.lua new file mode 100644 index 000000000..994d52ec0 --- /dev/null +++ b/xmake/core/project/addons.lua @@ -0,0 +1,188 @@ +--!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 addons.lua +-- + +-- define module +local addons = addons or {} + +-- load modules +local os = require("base/os") +local io = require("base/io") +local path = require("base/path") +local table = require("base/table") +local addon = require("package/addon") + +-- the file which declares the addons of a project, e.g. <projectdir>/xmake-addons.lua +-- +-- it's loaded before the project file, so that the addons are always installed when +-- we load the project, e.g. includes("@addon/esp32-devel/board") +-- +-- e.g. +-- add_addons("esp32-devel 1.0.x") +-- add_addons("serial-tools", {optional = true}) +-- +function addons.filename() + return "xmake-addons.lua" +end + +function addons.file(projectdir) + return path.join(projectdir or os.projectdir(), addons.filename()) +end + +-- the lock file of the declared addons, e.g. <projectdir>/xmake-addons.lock +-- +-- @note it's independent of `xmake-requires.lock`, the addons are always locked, +-- they provide the build scripts and we should never change them silently +-- +function addons.lockfile(projectdir) + return path.join(projectdir or os.projectdir(), "xmake-addons.lock") +end + +-- get the apis of the addons file +-- +-- @note it only declares which addons this project needs, the addon resources +-- are always referenced from the project file, e.g. add_rules("@addon/esp32-devel/app") +-- +function addons.apis() + return { + values = { + "add_addons" + } + } +end + +-- the interpreter of the addons file +function addons._interpreter() + local interp = addons._INTERPRETER + if interp == nil then + -- we need to load it lazily, the interpreter also depends on the addon module + local interpreter = require("base/interpreter") + interp = interpreter.new() + interp:api_define(addons.apis()) + -- the sub-projects declare their addons in this file too, + -- e.g. includes("sub") -> sub/xmake-addons.lua + interp:includes_rootfilename_set(addons.filename()) + -- and we cannot reference the addons here, they have not been installed yet, + -- e.g. includes("@addon/esp32-devel/board") + interp:includes_references_set(false, "please move it to the project file(xmake.lua)!") + addons._INTERPRETER = interp + end + return interp +end + +-- load the declared addons of the given project directory +-- +-- @return the addons information and errors, it will be nil if this project declares nothing, +-- e.g. {addons = {"esp32-devel 1.0.x"}, addons_extra = {...}} +-- +function addons.load(projectdir) + local filepath = addons.file(projectdir) + if not os.isfile(filepath) then + return + end + + -- enter the project directory, the include paths are relative to it, + -- e.g. includes("sub") + local oldir, errors = os.cd(path.directory(filepath)) + if not oldir then + return nil, errors + end + + local rootinfo + local interp = addons._interpreter() + local ok, errors = interp:load(filepath) + if ok then + rootinfo, errors = interp:make("root", true, true) + end + os.cd(oldir) + if not rootinfo then + return nil, errors + end + + local addonsinfo = {addons = table.wrap(rootinfo:get("addons")), + addons_extra = rootinfo:extraconf("addons")} + + -- check the declared addons + local declared = {} + for _, requirestr in ipairs(addonsinfo.addons) do + + -- this file is loaded before the addons are installed, so it cannot reference them + if requirestr:startswith("@") then + return nil, string.format("%s: cannot reference the addon resources(%s) here, please move it to the project file(xmake.lua)!", + filepath, requirestr) + end + + local name = requirestr:split("%s")[1] + if name == "addon" or name == "self" then + return nil, string.format("%s: the addon name(%s) is reserved by xmake for the addon references, please rename it!", + filepath, name) + end + if name == "." or name == ".." or name:find("[/\\:]") then + return nil, string.format("%s: invalid addon name(%s)!", filepath, name) + end + + -- we can only install one version of an addon for a project + if declared[name] then + return nil, string.format("%s: the addon(%s) is declared twice, e.g. `%s` and `%s`, please merge them!", + filepath, name, declared[name], requirestr) + end + declared[name] = requirestr + end + return addonsinfo +end + +-- get the locked addons, e.g. {["esp32-devel"] = {version = "1.0.3"}} +function addons.locked(projectdir) + local lockfile = addons.lockfile(projectdir) + if os.isfile(lockfile) then + local lockinfo = io.load(lockfile) + if lockinfo then + lockinfo.__meta__ = nil + return lockinfo + end + end +end + +-- are all the declared addons installed already? +-- +-- @note we need to check it in-process for every command which loads the project, +-- so we only check the locked versions here, the installer will resolve them again +-- +function addons.satisfied(addonsinfo, projectdir) + local locked = addons.locked(projectdir) + if not locked then + return false + end + local installed = addon.addons() + for _, requirestr in ipairs(addonsinfo.addons) do + local name = requirestr:split("%s")[1] + local lockinfo = locked[name] + if not lockinfo then + return false + end + local addoninfo = installed[addon.dirname(name)] + if not addoninfo or addoninfo.version ~= lockinfo.version then + return false + end + end + return true +end + +-- return module +return addons diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index 803bb5d9b..db269eda9 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -45,6 +45,8 @@ local option = require("project/option") local policy = require("project/policy") local project_package = require("project/package") local deprecated_project = require("project/deprecated/project") +local addon = require("package/addon") +local addons = require("project/addons") local package = require("package/package") local platform = require("platform/platform") local toolchain = require("tool/toolchain") @@ -227,6 +229,69 @@ function project._api_add_toolchaindirs(interp, ...) end end +-- install the addons which this project declares +-- +-- @note we cannot install them here, we are loading the project, so we do it in a +-- sub-process, @see xmake/modules/private/addons/main.lua +-- +function project._install_addons() + -- @note we need to cache the result, the project may be loaded many times, + -- otherwise the failure would be ignored by the next load + if not project._ADDONS_CHECKED then + project._ADDONS_CHECKED = true + project._ADDONS_OK, project._ADDONS_ERRORS = project._do_install_addons() + end + return project._ADDONS_OK, project._ADDONS_ERRORS +end + +-- do install the addons which this project declares +function project._do_install_addons() + if os.getenv("XMAKE_SKIP_ADDONS") then + return true + end + + -- this project declares nothing? or they have been installed already + local addonsinfo, errors = addons.load() + if errors then + return false, errors + end + if not addonsinfo or #addonsinfo.addons == 0 or addons.satisfied(addonsinfo) then + return true + end + + -- @note we run it in a temporary directory, otherwise it would load this project again + -- + -- @note we may be called when building the option menu, the command line has not + -- been parsed yet, so we can only get the common flags from the raw arguments + -- + local argv = {"lua"} + local flags = {["-y"] = "--yes", ["--yes"] = "--yes", + ["-v"] = "--verbose", ["--verbose"] = "--verbose", + ["-D"] = "--diagnosis", ["--diagnosis"] = "--diagnosis"} + local flags_added = {} + for _, arg in ipairs(xmake._COMMAND_ARGV or {}) do + local flag = flags[arg] + if flag and not flags_added[flag] then + table.insert(argv, flag) + flags_added[flag] = true + end + end + table.insert(argv, "private.addons") + table.insert(argv, os.projectdir()) + local envs = os.getenvs() + envs.XMAKE_SKIP_ADDONS = "y" + local ok, errors = os.execv(os.programfile(), argv, {curdir = os.tmpdir(), envs = envs}) + if ok ~= 0 then + return false, errors or "install the addons of this project failed!" + end + + -- we have loaded the registry and its caches before installing them, so we need to reload it + addon.reload() + rule.clear() + task.clear() + return true +end + -- load the project file function project._load(force, disable_filter) @@ -235,6 +300,14 @@ function project._load(force, disable_filter) return true end + -- install the addons which this project declares in `xmake-addons.lua` first, + -- it may use their rules, toolchains and includes files, + -- e.g. includes("@addon/esp32-devel/board") + local ok, errors = project._install_addons() + if not ok then + return false, errors + end + -- enter the project directory local oldir, errors = os.cd(os.projectdir()) if not oldir then diff --git a/xmake/core/sandbox/modules/import/core/project/addons.lua b/xmake/core/sandbox/modules/import/core/project/addons.lua new file mode 100644 index 000000000..9e866bd06 --- /dev/null +++ b/xmake/core/sandbox/modules/import/core/project/addons.lua @@ -0,0 +1,48 @@ +--!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 addons.lua +-- + +-- define module +local sandbox_core_project_addons = sandbox_core_project_addons or {} + +-- load modules +local addons = require("project/addons") +local raise = require("sandbox/modules/raise") + +-- inherit some builtin interfaces +sandbox_core_project_addons.file = addons.file +sandbox_core_project_addons.lockfile = addons.lockfile +sandbox_core_project_addons.locked = addons.locked +sandbox_core_project_addons.satisfied = addons.satisfied + +-- load the declared addons of the given project directory +-- +-- @param projectdir the project directory +-- @return the addons information, it will be nil if this project declares nothing +-- +function sandbox_core_project_addons.load(projectdir) + local addonsinfo, errors = addons.load(projectdir) + if errors then + raise(errors) + end + return addonsinfo +end + +-- return module +return sandbox_core_project_addons diff --git a/xmake/modules/private/action/require/impl/install_packages.lua b/xmake/modules/private/action/require/impl/install_packages.lua index 7ab247351..2d633109e 100644 --- a/xmake/modules/private/action/require/impl/install_packages.lua +++ b/xmake/modules/private/action/require/impl/install_packages.lua @@ -211,6 +211,8 @@ function _get_confirm(packages, opt) -- show tips if opt.toolchain then cprint("${bright color.warning}note: ${clear}install or modify (m) these ${bright}toolchain${clear} packages first (pass -y to skip confirm)?") + elseif opt.packagekind == "addon" then + cprint("${bright color.warning}note: ${clear}install or modify (m) these ${bright}addons${clear} (pass -y to skip confirm)?") else cprint("${bright color.warning}note: ${clear}install or modify (m) these packages (pass -y to skip confirm)?") end diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua index 65d5f4153..1105384c8 100644 --- a/xmake/modules/private/action/require/impl/package.lua +++ b/xmake/modules/private/action/require/impl/package.lua @@ -1702,6 +1702,9 @@ function get_configs_str(package) end if requireinfo.kind then table.insert(configs, requireinfo.kind) + elseif package:is_addon() then + -- @note the kind is only set for the dependencies, e.g. add_deps("foo", {kind = "addon"}) + table.insert(configs, "addon") end local ignored_configs_for_buildhash = hashset.from(requireinfo.ignored_configs_for_buildhash or {}) local configs_overrided = requireinfo.configs_overrided or {} diff --git a/xmake/modules/private/addons/main.lua b/xmake/modules/private/addons/main.lua new file mode 100644 index 000000000..43a623e20 --- /dev/null +++ b/xmake/modules/private/addons/main.lua @@ -0,0 +1,92 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file main.lua +-- + +-- imports +import("core.base.option") +import("core.package.addon") +import("core.project.addons") + +-- the format version of the addons lock file +local LOCKVERSION = "1.0" + +-- get the requires of the declared addons +-- +-- @note we always install the locked versions, the declared versions are only used +-- to resolve them when there is no lock yet, @see _lock_addons +-- +function _get_requires(addonsinfo, locked) + local requires = {} + for _, requirestr in ipairs(addonsinfo.addons) do + local name = requirestr:split("%s")[1] + local lockinfo = locked and locked[name] + if lockinfo and lockinfo.version then + requirestr = name .. " " .. lockinfo.version + end + table.insert(requires, requirestr) + end + return requires +end + +-- lock the installed addons, so that we always get the same ones +-- +-- @note we get the installed versions from the addons registry, they are +-- registered when installing them, @see core/package/addon.lua +-- +function _lock_addons(projectdir, addonsinfo) + local lockinfo = {} + -- @note we need to reload the registry, they have been installed by another process + local installed = addon.addons({force = true}) + for _, requirestr in ipairs(addonsinfo.addons) do + local name = requirestr:split("%s")[1] + local addoninfo = installed[addon.dirname(name)] + if addoninfo then + lockinfo[name] = {version = addoninfo.version} + end + end + lockinfo.__meta__ = {version = LOCKVERSION} + io.save(addons.lockfile(projectdir), lockinfo, {orderkeys = true}) +end + +-- install the addons which the given project declares in its `xmake-addons.lua` +-- +-- @note we are called from a sub-process, the project cannot be loaded until its +-- addons are installed, @see core/project/project.lua +-- +function main(projectdir) + projectdir = projectdir or os.projectdir() + local addonsinfo = addons.load(projectdir) + if not addonsinfo or #addonsinfo.addons == 0 then + return + end + + -- install them with xrepo, it installs the packages in its own working directory, + -- so we need not a project here + local argv = {"lua", "private.xrepo", "install", "--addon"} + for _, name in ipairs({"yes", "verbose", "diagnosis"}) do + if option.get(name) then + table.insert(argv, "--" .. name) + end + end + table.join2(argv, _get_requires(addonsinfo, addons.locked(projectdir))) + os.execv(os.programfile(), argv) + + -- and lock them, so that the other users get the same versions + _lock_addons(projectdir, addonsinfo) +end |
