diff options
| author | ruki <[email protected]> | 2026-08-16 22:00:38 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-08-16 22:00:38 +0800 |
| commit | dd7d4521ca5008a2c53401ec550f00c56c09435a (patch) | |
| tree | d3db09204acba4aaba1b1d31d3931ff6fcef23e1 | |
| parent | f53769382d8f65c6a90a838da55287864888cfbb (diff) | |
improve interpreter
| -rw-r--r-- | xmake/actions/addon/main.lua | 2 | ||||
| -rw-r--r-- | xmake/core/base/interpreter.lua | 42 | ||||
| -rw-r--r-- | xmake/core/project/project.lua | 33 |
3 files changed, 34 insertions, 43 deletions
diff --git a/xmake/actions/addon/main.lua b/xmake/actions/addon/main.lua index 28c73a754..99f92d125 100644 --- a/xmake/actions/addon/main.lua +++ b/xmake/actions/addon/main.lua @@ -21,6 +21,7 @@ -- imports import("core.base.option") import("core.package.addon") +import("core.project.project") import("devel.git") import("private.action.addon.impl.install_addons") import("private.action.addon.impl.xrepo", {alias = "xrepo_addon"}) @@ -230,7 +231,6 @@ end -- upgrade the addons which the current project declares, e.g. add_addons("esp32-devel 1.0.x") function _upgrade() - import("core.project.project") local declarations = {addons = table.wrap(project.get("addons")), repositories = table.wrap(project.get("repositories"))} assert(#declarations.addons > 0, "no addons are declared in this project, e.g. add_addons(\"esp32-devel\")!") diff --git a/xmake/core/base/interpreter.lua b/xmake/core/base/interpreter.lua index 215ff6673..ad62168fb 100644 --- a/xmake/core/base/interpreter.lua +++ b/xmake/core/base/interpreter.lua @@ -880,31 +880,24 @@ function interpreter:scriptdir() return path.directory(self._PRIVATE._CURFILE) end --- set root scope kind --- --- the root api will affect these scopes --- --- do we defer the unresolvable addon references? e.g. includes("@addon/esp32/board") --- --- @note the project file declares the addons which it needs, but we can only know them --- after loading it, so the first load must survive the references of the addons which --- are not installed yet, @see project._load() --- -function interpreter:addons_deferred() - return self._PRIVATE._ADDONS_DEFERRED -end - --- defer the unresolvable addon references instead of raising errors -function interpreter:addons_deferred_set(enabled) - self._PRIVATE._ADDONS_DEFERRED = enabled - self._PRIVATE._ADDONS_MISSING = nil +-- do we ignore the unresolvable references of includes()? e.g. includes("@addon/esp32/board") +function interpreter:includes_unresolved() + return self._PRIVATE._INCLUDES_UNRESOLVED end --- get the addon references which have not been resolved, @see interpreter:addons_deferred_set -function interpreter:addons_missing() - return self._PRIVATE._ADDONS_MISSING +-- ignore the unresolvable references of includes() instead of raising errors +-- +-- @note the project file may reference the resources which have not been installed yet, +-- so the caller can load it, install them and load it again, @see project._load() +-- +function interpreter:includes_unresolved_set(enabled) + self._PRIVATE._INCLUDES_UNRESOLVED = enabled end +-- set root scope kind +-- +-- the root api will affect these scopes +-- function interpreter:rootscope_set(scope_kind) assert(self and self._PRIVATE) self._PRIVATE._ROOTSCOPE = scope_kind @@ -1821,11 +1814,8 @@ end function interpreter:_find_addon_includes(subpath) local referenceinfo, errors = addon.resolve_reference(subpath, "/", "includes", {scriptdir = self:scriptdir()}) if not referenceinfo then - -- this addon is not installed yet? we will install it and load this file again - if self:addons_deferred() then - local missing = self._PRIVATE._ADDONS_MISSING or {} - table.insert(missing, subpath) - self._PRIVATE._ADDONS_MISSING = missing + -- it has not been installed yet? the caller may install it and load this file again + if self:includes_unresolved() then return {} end os.raise(errors) diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index a9b843ef7..f67ac9a67 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -237,11 +237,12 @@ end function project._install_addons(rootinfo) -- @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._ADDONS_INSTALLED = project._do_install_addons(rootinfo) + local result = project._ADDONS_RESULT + if result == nil then + result = project._do_install_addons(rootinfo) + project._ADDONS_RESULT = result end - return project._ADDONS_OK, project._ADDONS_ERRORS, project._ADDONS_INSTALLED + return result end -- activate the addon versions which this project locks @@ -265,23 +266,23 @@ end -- do install the addons which this project declares -- install the addons which this project declares, e.g. add_addons("esp32-devel 1.0.x") -- --- @return true, errors and installed +-- @return the result, e.g. {ok = true, installed = true}, {ok = false, errors = ".."} -- function project._do_install_addons(rootinfo) -- this project declares nothing? local requires = table.wrap(rootinfo:get("addons")) if #requires == 0 then - return true + return {ok = true} end local ok, errors = addons.validate(requires) if not ok then - return false, errors + return {ok = false, errors = errors} end -- they have been installed already? if addons.satisfied(requires) then - return true + return {ok = true} end -- tell the user why we are installing something, it may need to confirm and download, @@ -299,7 +300,7 @@ function project._do_install_addons(rootinfo) local datafile = os.tmpfile() local ok, errors = io.save(datafile, {addons = requires, repositories = table.wrap(rootinfo:get("repositories"))}) if not ok then - return false, errors + return {ok = false, errors = errors} end -- @note we run it in a working directory which has no project, @see addon.workdir(), @@ -326,7 +327,7 @@ function project._do_install_addons(rootinfo) local exitcode, errors = os.execv(os.programfile(), argv, {curdir = addon.workdir()}) os.rm(datafile) if exitcode ~= 0 then - return false, errors or "install the addons of this project failed!" + return {ok = false, errors = 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 @@ -334,7 +335,7 @@ function project._do_install_addons(rootinfo) project._pin_addons() rule.clear() task.clear() - return true, nil, true + return {ok = true, installed = true} end -- load the project file @@ -369,7 +370,7 @@ function project._load(opt) -- but we can only know them after loading it, so this pass must survive the references -- of the addons which are not installed yet, and we load it again after installing them, -- e.g. includes("@addon/esp32-devel/board") - interp:addons_deferred_set(not opt.addons_installed) + interp:includes_unresolved_set(not opt.addons_installed) -- load script local ok, errors = interp:load(project.rootfile(), {on_load_data = function (data) @@ -399,12 +400,12 @@ function project._load(opt) -- best-effort way and every command builds it, @see project._load_tasks() -- if not opt.skip_addons and not opt.addons_installed then - local ok, errors, installed = project._install_addons(rootinfo) - if not ok then + local result = project._install_addons(rootinfo) + if not result.ok then os.cd(oldir) - return false, errors + return false, result.errors end - if installed then + if result.installed then os.cd(oldir) return project._load({force = true, disable_filter = opt.disable_filter, addons_installed = true}) end |
