summaryrefslogtreecommitdiff
path: root/xmake/core/base
diff options
context:
space:
mode:
Diffstat (limited to 'xmake/core/base')
-rw-r--r--xmake/core/base/interpreter.lua95
-rw-r--r--xmake/core/base/poller.lua16
-rw-r--r--xmake/core/base/scheduler.lua14
-rw-r--r--xmake/core/base/task.lua36
4 files changed, 80 insertions, 81 deletions
diff --git a/xmake/core/base/interpreter.lua b/xmake/core/base/interpreter.lua
index 01b689fe6..8dc0e4880 100644
--- a/xmake/core/base/interpreter.lua
+++ b/xmake/core/base/interpreter.lua
@@ -30,7 +30,6 @@ local string = require("base/string")
local hashset = require("base/hashset")
local scopeinfo = require("base/scopeinfo")
local deprecated = require("base/deprecated")
-local addon = require("package/addon")
local sandbox = require("sandbox/sandbox")
-- the rules to reword the raw lua error messages into friendly ones, {pattern, replacement}
@@ -880,40 +879,37 @@ function interpreter:scriptdir()
return path.directory(self._PRIVATE._CURFILE)
end
--- set root scope kind
---
--- the root api will affect these scopes
+-- add a resolver for the references of includes(), e.g. includes("@addon/esp32/check")
--
--- 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
+-- @param resolver function (interp, reference), it returns the files, or nil and errors
--
--- e.g. interp:includes_rootfilename_set("xmake-addons.lua") -> includes("subdir") -> subdir/xmake-addons.lua
+-- @note the interpreter knows nothing about the references, the callers register the
+-- resolvers which they support, e.g. @see project._interpreter()
--
-function interpreter:includes_rootfilename_set(filename)
- self._PRIVATE._INCLUDES_ROOTFILENAME = filename
+function interpreter:includes_resolver_add(resolver)
+ local resolvers = self._PRIVATE._INCLUDES_RESOLVERS or {}
+ table.insert(resolvers, resolver)
+ self._PRIVATE._INCLUDES_RESOLVERS = resolvers
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
+-- do we ignore the unresolvable references of includes()? e.g. includes("@addon/esp32/board")
+function interpreter:includes_unresolved()
+ return self._PRIVATE._INCLUDES_UNRESOLVED
end
--- enable/disable the referenced files of includes()
---
--- @param enabled enable them or not
--- @param hint the extra hint of the error message
+-- ignore the unresolvable references of includes() instead of raising errors
--
--- @note the addons file is loaded before the addons are installed, so it cannot reference them
+-- @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_references_set(enabled, hint)
- self._PRIVATE._INCLUDES_REFERENCES = enabled
- self._PRIVATE._INCLUDES_REFERENCES_HINT = hint
+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
@@ -1826,26 +1822,6 @@ function interpreter:_find_builtin_includes(subpath)
return os.files(path.join(os.programdir(), "includes", builtin_path, "xmake.lua"))
end
--- find the include files of the addons, e.g. includes("@addon/esp32/check"), includes("@self/check")
-function interpreter:_find_addon_includes(subpath)
- local referenceinfo, errors = addon.resolve_reference(subpath, "/", "includes", {scriptdir = self:scriptdir()})
- if not referenceinfo then
- os.raise(errors)
- end
- local addon_path = referenceinfo.name
- local files
- if addon_path:endswith(".lua") then
- files = os.files(path.join(referenceinfo.dir, addon_path))
- else
- files = os.files(path.join(referenceinfo.dir, addon_path, "xmake.lua"))
- end
- -- the addon is installed, but it does not provide this file, we cannot ignore it
- if not files or #files == 0 then
- os.raise("includes(%s) not found!", subpath)
- end
- return files
-end
-
function interpreter:api_builtin_includes(...)
assert(self and self._PRIVATE and self._PRIVATE._ROOTDIR and self._PRIVATE._MTIMES)
local curfile = self._PRIVATE._CURFILE
@@ -1856,12 +1832,6 @@ 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
@@ -1871,11 +1841,24 @@ function interpreter:api_builtin_includes(...)
found = true
end
end
- -- attempt to find files from the includes of the addons
- -- e.g. includes("@addon/esp32/check"), includes("@self/check")
- if not found and addon.is_reference(subpath, "/") then
- table.join2(subpaths_matched, self:_find_addon_includes(subpath))
- found = true
+ -- attempt to find files from the registered resolvers of the references
+ -- e.g. includes("@addon/esp32/check"), @see interpreter:includes_resolver_add()
+ if not found and subpath:startswith("@") then
+ for _, resolver in ipairs(self._PRIVATE._INCLUDES_RESOLVERS or {}) do
+ local files, errors = resolver(self, subpath)
+ if files then
+ table.join2(subpaths_matched, files)
+ found = true
+ break
+ elseif errors then
+ -- it has not been resolved yet? the caller may load this file again
+ if self:includes_unresolved() then
+ found = true
+ break
+ end
+ os.raise(errors)
+ end
+ end
end
-- find the given files from the project directory
if not found then
@@ -1884,7 +1867,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, self:includes_rootfilename()))
+ files = os.files(path.join(subpath, "xmake.lua"))
end
if files and #files > 0 then
table.join2(subpaths_matched, files)
diff --git a/xmake/core/base/poller.lua b/xmake/core/base/poller.lua
index bb18dfda4..43c3bea1a 100644
--- a/xmake/core/base/poller.lua
+++ b/xmake/core/base/poller.lua
@@ -124,7 +124,7 @@ function poller:remove(obj)
end
-- remove poller object data
- self:_pollerdata_set(obj, nil)
+ self:_pollerdata_set(obj:cdata(), nil)
return true
end
@@ -153,13 +153,15 @@ function poller:wait(timeout)
local otype = v[1]
local cdata = v[2]
local events = v[3]
- local pollerdata = self:_pollerdata(cdata)
- if not pollerdata then
- return -1, string.format("no object data for cdata(%s)!", cdata)
+ -- this object may have been removed from the poller while its event
+ -- was already collected, e.g. a pending overlapped io on windows,
+ -- we just drop it, it has no owner any more, @see poller:remove()
+ local pollerdata = self:_pollerdata(cdata)
+ if pollerdata then
+ local obj = pollerdata[1]
+ assert(obj and obj:otype() == otype and obj:cdata() == cdata)
+ table.insert(results, {obj, events, pollerdata[2]})
end
- local obj = pollerdata[1]
- assert(obj and obj:otype() == otype and obj:cdata() == cdata)
- table.insert(results, {obj, events, pollerdata[2]})
end
end
return count, results
diff --git a/xmake/core/base/scheduler.lua b/xmake/core/base/scheduler.lua
index a59c38207..4ae8d74e0 100644
--- a/xmake/core/base/scheduler.lua
+++ b/xmake/core/base/scheduler.lua
@@ -304,9 +304,17 @@ end
function scheduler:_poller_events_cb(obj, events)
-- get poller object data
+ --
+ -- the object may have been cancelled while its event was already queued,
+ -- e.g. a process which exits right after we stopped waiting for it,
+ -- @see scheduler:poller_cancel()
+ --
+ -- such an event has no owner any more, we just drop it: it is not an
+ -- error of the scheduler and it must not abort the whole loop
local pollerdata = self:_poller_data(obj)
if not pollerdata then
- return false, string.format("%s: cannot get poller data!", obj)
+ utils.dprint("%s: drop the event(%d), it has been cancelled!", obj, events)
+ return true
end
-- is process/fwatcher object?
@@ -1068,6 +1076,10 @@ function scheduler:poller_waitproc(obj, timeout)
running:waitobj_set(obj)
-- wait
+ --
+ -- @note we keep this process in the poller if it is timeout, so its exit status
+ -- is still saved as a pending status when it exits later, and the next wait
+ -- returns it immediately, @see scheduler:_poller_events_cb()
local ok = self:co_suspend()
return ok, pollerdata.object_event
end
diff --git a/xmake/core/base/task.lua b/xmake/core/base/task.lua
index 2cd1fa58a..2b3788603 100644
--- a/xmake/core/base/task.lua
+++ b/xmake/core/base/task.lua
@@ -24,6 +24,7 @@ local task = task or {}
-- load modules
local os = require("base/os")
local table = require("base/table")
+local utils = require("base/utils")
local string = require("base/string")
local global = require("base/global")
local hashset = require("base/hashset")
@@ -84,17 +85,18 @@ end
function task._directories()
local dirs = task._DIRECTORIES
if dirs == nil then
- dirs = {
- path.join(global.directory(), "plugins"),
- path.join(os.programdir(), "plugins"),
- path.join(os.programdir(), "actions")}
-
- -- add the plugins of the installed addons, e.g. ~/.xmake/addons/<name>/<version>/plugins
+ -- add the plugins of the installed addons first, e.g. ~/.xmake/addons/<name>/<version>/plugins
--
-- we get them from the addons registry file directly,
-- so we do not need to scan the whole addons directory on startup
--
- table.join2(dirs, addon.payloads("plugins"))
+ -- @note the first one wins, so an addon is able to take over a deprecated
+ -- builtin plugin, e.g. `xmake format`
+ --
+ dirs = addon.payloads("plugins")
+ table.insert(dirs, path.join(global.directory(), "plugins"))
+ table.insert(dirs, path.join(os.programdir(), "plugins"))
+ table.insert(dirs, path.join(os.programdir(), "actions"))
task._DIRECTORIES = dirs
end
return dirs
@@ -401,8 +403,9 @@ end
-- is the given plugin conflicting with the loaded one?
--
--- the plugins are not namespaced, so we need to report the conflicts of the addons,
--- otherwise we do not know which plugin will be run
+-- the plugins are not namespaced, so the first one always wins, @see task._directories(),
+-- but we need to report the conflicts of the addons, otherwise we do not know which
+-- plugin will be run
--
-- @param taskname the task name
-- @param taskfile the task file of the loaded plugin, it will be nil if it's the first one
@@ -413,16 +416,15 @@ function task._is_conflicting(taskname, taskfile, filepath)
return false
end
- -- we only report it if one of them comes from an addon, the builtin plugins
- -- and the plugins in the global directory are always overridable
- local addondir = path.absolute(addon.installdir())
- if not path.absolute(taskfile):startswith(addondir) and not path.absolute(filepath):startswith(addondir) then
- return false
- end
-
+ -- we only report it if both of them come from the addons, taking over a builtin
+ -- plugin is expected, e.g. `xmake format` has been moved to an addon
+ --
-- @note we cannot raise errors here, otherwise all the commands will be broken,
-- and the user cannot even remove the conflicting addons
- utils.warning("plugin(%s) conflicts, we will use the first one!\n -> %s\n -> %s", taskname, taskfile, filepath)
+ local addondir = path.absolute(addon.installdir())
+ if path.absolute(taskfile):startswith(addondir) and path.absolute(filepath):startswith(addondir) then
+ utils.warning("plugin(%s) conflicts, we will use the first one!\n -> %s\n -> %s", taskname, taskfile, filepath)
+ end
return true
end