diff options
Diffstat (limited to 'xmake')
41 files changed, 690 insertions, 302 deletions
diff --git a/xmake/actions/addon/main.lua b/xmake/actions/addon/main.lua index bf6f27f5d..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"}) @@ -228,9 +229,12 @@ function _remove() end end --- upgrade the addons which the current project declares in its `xmake-addons.lua` +-- upgrade the addons which the current project declares, e.g. add_addons("esp32-devel 1.0.x") function _upgrade() - install_addons(os.projectdir(), {upgrade = true}) + 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\")!") + install_addons(os.projectdir(), declarations, {upgrade = true}) end -- search the addons from the repositories 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 diff --git a/xmake/core/package/addon.lua b/xmake/core/package/addon.lua index 6e7d62eb4..d6798f56f 100644 --- a/xmake/core/package/addon.lua +++ b/xmake/core/package/addon.lua @@ -168,12 +168,19 @@ function addon._check_conflicts(dirname, addoninfo) end -- the global modules can also conflict with the builtin and the user modules + -- + -- @note the `core.*` modules are in the core directory of the sandbox, + -- they are not in `<programdir>/modules`, + -- @see core/sandbox/modules/import/core/sandbox/module.lua + local moduledirs = {path.join(os.programdir(), "modules"), + path.join(os.programdir(), "core", "sandbox", "modules", "import"), + path.join(global.directory(), "modules")} for _, name in ipairs(addoninfo.globalmodules or {}) do local modulepath = (name:gsub("%.", "/")) .. ".lua" - for _, moduledir in ipairs({os.programdir(), global.directory()}) do - if os.isfile(path.join(moduledir, "modules", modulepath)) then + for _, moduledir in ipairs(moduledirs) do + if os.isfile(path.join(moduledir, modulepath)) then return string.format("global module(%s) conflicts, it has been provided by %s!\nplease rename it in the addon manifest.", - name, moduledir == os.programdir() and "xmake" or path.join(moduledir, "modules")) + name, moduledir:startswith(os.programdir()) and "xmake" or moduledir) end end end @@ -566,6 +573,36 @@ function addon.globalmodules() return globalmodules end +-- find the include files of the given addon reference, e.g. includes("@addon/esp32/board") +-- +-- @param interp the interpreter which is loading the file, @see interpreter:includes_resolver_add +-- @param reference the reference, e.g. "@addon/esp32/board", "@self/board" +-- +-- @return the files, or nil and errors +-- +function addon.find_includes(interp, reference) + if not addon.is_reference(reference, "/") then + return + end + local referenceinfo, errors = addon.resolve_reference(reference, "/", "includes", {scriptdir = interp:scriptdir()}) + if not referenceinfo then + return nil, errors + end + local name = referenceinfo.name + local files + if name:endswith(".lua") then + files = os.files(path.join(referenceinfo.dir, name)) + else + files = os.files(path.join(referenceinfo.dir, name, "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!", reference) + end + return files +end + -- get the payload directories of the given kind from all installed addons -- -- @param kind the payload kind, e.g. "plugins", "rules" diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index 78268351e..3d98a3743 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -2117,11 +2117,17 @@ function _instance:fetch(opt) -- always install to the local project directory? -- @see https://github.com/xmake-io/xmake/pull/4376 + -- + -- @note the host packages are the tools which build the other packages, e.g. the toolchains, + -- they do not depend on the project configuration and they are shared between the projects, + -- so they are only installed locally with their own policy + -- @see https://github.com/xmake-io/xmake/issues/7716 + local policyname = self:is_host() and "package.host.install_locally" or "package.install_locally" local install_locally - if project and project.policy("package.install_locally") then + if project and project.policy(policyname) then install_locally = true end - if install_locally == nil and self:policy("package.install_locally") then + if install_locally == nil and self:policy(policyname) then install_locally = true end if not self:is_local() and install_locally and system ~= true then @@ -2529,9 +2535,20 @@ function _instance:_generate_runtime_configs(sourcekind) self.sourcekinds = function (self) return sourcekind end - configs.cxflags = self:compiler(sourcekind):map_flags("runtime", runtimes, {target = self}) - configs.ldflags = self:linker("binary", sourcekind):map_flags("runtime", runtimes, {target = self}) - configs.shflags = self:linker("shared", sourcekind):map_flags("runtime", runtimes, {target = self}) + local cxflags = self:compiler(sourcekind):map_flags("runtime", runtimes, {target = self}) + local ldflags = self:linker("binary", sourcekind):map_flags("runtime", runtimes, {target = self}) + local shflags = self:linker("shared", sourcekind):map_flags("runtime", runtimes, {target = self}) + + -- @note the multi-argument flags must be checked as a whole, e.g. the clang runtime + -- flags on windows, `-Xclang --dependent-lib=xxx` would be broken if + -- `--dependent-lib=xxx` is checked and dropped separately + -- @see https://github.com/xmake-io/xmake/issues/7704 + local group = function (flags) + return flags and #flags > 1 and {table.wrap_lock(flags)} or flags + end + configs.cxflags = group(cxflags) + configs.ldflags = group(ldflags) + configs.shflags = group(shflags) self.sourcekinds = nil end return configs @@ -3099,12 +3116,20 @@ end -- -- @param opt the options, e.g. {localdir = true} -- - localdir: return the local project packages directory (build/.packages) --- instead of the global directory (~/.xmake/packages) +-- instead of the global directory (~/.xmake/packages), +-- it can be overridden with `XMAKE_PKG_LOCALDIR` -- -- @return the install directory path -- function package.installdir(opt) if opt and opt.localdir then + -- the parent process passes its local directory to the sub-process which builds + -- a package, so the packages it installs locally land in the same place and are + -- not installed twice, @see https://github.com/xmake-io/xmake/issues/7716 + local localdir = os.getenv("XMAKE_PKG_LOCALDIR") + if localdir then + return path.normalize(path.absolute(localdir)) + end return path.join(config.builddir({absolute = true}), ".packages") end local installdir = package._INSTALLDIR diff --git a/xmake/core/platform/menu.lua b/xmake/core/platform/menu.lua index cec901ee8..77a57381d 100644 --- a/xmake/core/platform/menu.lua +++ b/xmake/core/platform/menu.lua @@ -42,7 +42,7 @@ function _remote_build_is_connected() local projectdir = os.projectdir() local projectfile = os.projectfile() if projectfile and os.isfile(projectfile) and projectdir then - local workdir = path.join(config.directory(), "remote_build") + local workdir = path.join(config.directory(), "service", "remote_build") local statusfile = path.join(workdir, "status.txt") if os.isfile(statusfile) then local status = io.load(statusfile) diff --git a/xmake/core/project/addons.lua b/xmake/core/project/addons.lua index 169b7d033..c979a6f8d 100644 --- a/xmake/core/project/addons.lua +++ b/xmake/core/project/addons.lua @@ -29,23 +29,6 @@ local table = require("base/table") local semver = require("base/semver") 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", "serial-tools") --- add_repositories("myrepo [email protected]:me/myrepo.git") --- -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, @@ -63,100 +46,29 @@ function addons.lockfile_version() return "1.0" 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" - -- the repositories which provide them, e.g. add_repositories("myrepo [email protected]:me/myrepo.git") - , "add_repositories" - } - } -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 +-- check the addons which a project declares, e.g. add_addons("esp32-devel 1.0.x") -- --- @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 = {...}} +-- @return true, or false and errors -- -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"), - repositories = table.wrap(rootinfo:get("repositories"))} - - -- check the declared addons +function addons.validate(requires) 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 - + for _, requirestr in ipairs(requires) do local name = addons.requirename(requirestr) 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) + return false, string.format("add_addons(%s): the name is reserved by xmake for the addon references, please rename it!", name) end if name == "." or name == ".." or name:find("[/\\:]") then - return nil, string.format("%s: invalid addon name(%s)!", filepath, name) + return false, string.format("add_addons(%s): invalid addon name!", 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) + return false, string.format("add_addons(%s): it is declared twice, e.g. `%s` and `%s`, please merge them!", + name, declared[name], requirestr) end declared[name] = requirestr end - return addonsinfo + return true end -- split the given declaration into the name and the version range @@ -204,13 +116,13 @@ end -- @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) +function addons.satisfied(requires, projectdir) local locked = addons.locked(projectdir) if not locked then return false end local installed = addon.addons() - for _, requirestr in ipairs(addonsinfo.addons) do + for _, requirestr in ipairs(requires) do local name = addons.requirename(requirestr) local lockinfo = locked[name] if not addons.locked_valid(requirestr, lockinfo) then diff --git a/xmake/core/project/policy.lua b/xmake/core/project/policy.lua index 617a792d6..81a8b6924 100644 --- a/xmake/core/project/policy.lua +++ b/xmake/core/project/policy.lua @@ -158,6 +158,12 @@ function policy.policies() ["package.install_always"] = {description = "Always install packages every time.", type = "boolean"}, -- Install packages in the local project folder ["package.install_locally"] = {description = "Install packages in the local project folder.", default = false, type = "boolean"}, + -- Install the host packages in the local project folder + -- + -- the host packages are the tools which build the other packages, e.g. the toolchains, + -- they do not depend on the project configuration and they are shared between the projects, + -- so they have their own policy, @see https://github.com/xmake-io/xmake/issues/7716 + ["package.host.install_locally"] = {description = "Install the host packages in the local project folder.", default = false, type = "boolean"}, -- Keep package source code after installing (disable source dir cleanup) ["package.keep_source"] = {description = "Keep package source code after installing.", default = false, type = "boolean"}, -- Set custom headers when downloading package diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index 0cda89db1..b92c5cfab 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -234,14 +234,15 @@ end -- @note we cannot install them here, we are loading the project, so we do it in a -- sub-process, @see xmake/modules/private/action/addon/impl/install_addons.lua -- -function project._install_addons() +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._do_install_addons() + 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 + return result end -- activate the addon versions which this project locks @@ -263,33 +264,45 @@ function project._pin_addons() end -- do install the addons which this project declares -function project._do_install_addons() +-- install the addons which this project declares, e.g. add_addons("esp32-devel 1.0.x") +-- +-- @return the result, e.g. {ok = true, installed = true}, {ok = false, errors = ".."} +-- +function project._do_install_addons(rootinfo) -- this project declares nothing? - local addonsinfo, errors = addons.load() - if errors then - return false, errors + local requires = table.wrap(rootinfo:get("addons")) + if #requires == 0 then + return {ok = true} end - if not addonsinfo or #addonsinfo.addons == 0 then - return true + local ok, errors = addons.validate(requires) + if not ok then + return {ok = false, errors = errors} end -- they have been installed already? - project._pin_addons() - if addons.satisfied(addonsinfo) then - return true + if addons.satisfied(requires) then + return {ok = true} end -- tell the user why we are installing something, it may need to confirm and download, -- e.g. `xmake --help` in a project directory which declares some addons - utils.cprint("${color.warning}note: ${clear}%s: this project needs the addons(${bright}%s${clear}), installing them ..", - addons.filename(), table.concat(addonsinfo.addons, ", ")) + utils.cprint("${color.warning}note: ${clear}this project needs the addons(${bright}%s${clear}), installing them ..", + table.concat(requires, ", ")) if baseoption.get("help") then -- the help menu also shows the options which the addons provide, but the user -- did not ask for an installation, so we tell them how to skip it utils.cprint("${dim}we can run it outside of the project directory to skip the installation${clear}") end + -- we pass the declarations to the installer, it must not load this project again, + -- @see xmake/modules/private/action/addon/impl/install_addons.lua + local datafile = os.tmpfile() + local ok, errors = io.save(datafile, {addons = requires, repositories = table.wrap(rootinfo:get("repositories"))}) + if not ok then + return {ok = false, errors = errors} + end + -- @note we run it in a working directory which has no project, @see addon.workdir(), -- otherwise it would load this project again -- @@ -310,9 +323,11 @@ function project._do_install_addons() end table.insert(argv, "private.action.addon.impl.install_addons") table.insert(argv, os.projectdir()) - local ok, errors = os.execv(os.programfile(), argv, {curdir = addon.workdir()}) - if ok ~= 0 then - return false, errors or "install the addons of this project failed!" + table.insert(argv, datafile) + local exitcode, errors = os.execv(os.programfile(), argv, {curdir = addon.workdir()}) + os.rm(datafile) + if exitcode ~= 0 then + 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 @@ -320,7 +335,7 @@ function project._do_install_addons() project._pin_addons() rule.clear() task.clear() - return true + return {ok = true, installed = true} end -- load the project file @@ -329,26 +344,13 @@ end -- - force: load the project file again even if it has been loaded -- - disable_filter: disable the interpreter filter, e.g. `$(plat)` -- - skip_addons: do not install the addons which this project declares +-- - addons_installed: the addons have been installed, we are loading it again -- function project._load(opt) opt = opt or {} - -- 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") - -- - -- @note we need to check it before the cache, the project file may have been loaded - -- already without them, e.g. by the option menu - -- - if opt.skip_addons then - -- we do not install them here, but we still need to use the locked versions - project._pin_addons() - else - local ok, errors = project._install_addons() - if not ok then - return false, errors - end - end + -- use the locked versions of the addons which this project declares + project._pin_addons() -- has already been loaded? if project._memcache():get("rootinfo") and not opt.force then @@ -364,6 +366,12 @@ function project._load(opt) -- get interpreter local interp = project.interpreter() + -- this project declares the addons which it needs, e.g. add_addons("esp32-devel"), + -- 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:includes_unresolved_set(not opt.addons_installed) + -- load script local ok, errors = interp:load(project.rootfile(), {on_load_data = function (data) for _, xmakerc_file in ipairs(project.rcfiles()) do @@ -386,6 +394,23 @@ function project._load(opt) return false, errors end + -- install the addons which this project declares, and then load it again with them + -- + -- @note we do not install them for the option menu, it merges the project tasks in a + -- best-effort way and every command builds it, @see project._load_tasks() + -- + if not opt.skip_addons and not opt.addons_installed then + local result = project._install_addons(rootinfo) + if not result.ok then + os.cd(oldir) + return false, result.errors + end + if result.installed then + os.cd(oldir) + return project._load({force = true, disable_filter = opt.disable_filter, addons_installed = true}) + end + end + -- load the root info of the target local rootinfo_target, errors = project._load_scope("root.target", true, not opt.disable_filter) if not rootinfo_target then @@ -780,6 +805,9 @@ function project.apis() , "add_requires" , "add_requireconfs" , "add_repositories" + -- the addons which this project needs, they are installed automatically, + -- e.g. add_addons("esp32-devel 1.0.x"), @see core/project/addons.lua + , "add_addons" } , paths = { @@ -832,6 +860,10 @@ function project.interpreter() -- set root scope interp:rootscope_set("target") + -- the project file can reference the includes files of the addons, + -- e.g. includes("@addon/esp32-devel/board") + interp:includes_resolver_add(addon.find_includes) + -- define apis for rule interp:api_define(rule.apis()) diff --git a/xmake/core/project/target.lua b/xmake/core/project/target.lua index 81b95cc96..e51ffeb8b 100644 --- a/xmake/core/project/target.lua +++ b/xmake/core/project/target.lua @@ -3296,6 +3296,14 @@ function target.linkname(filename, opt) linkname, count = filename:gsub(target.filename("__pattern__", "static", {plat = "windows"}):gsub("%.", "%%."):gsub("__pattern__", "(.+)") .. "$", "%1") end if count > 0 and linkname then + -- the library name itself may end with `.lib`, e.g. vcpkg installs `Luau.CLI.lib.lib`, + -- we cannot strip it, nf_link() passes the names ending with `.lib` to the linker + -- as-is, and it would look for `Luau.CLI.lib` + -- + -- @see https://github.com/xmake-io/xmake/issues/7708 + if opt.plat == "windows" and linkname:endswith(".lib") then + return filename + end return linkname end -- fallback to the generic unix library name, libxxx.a, libxxx.so, .. diff --git a/xmake/core/sandbox/modules/import/core/base/scheduler.lua b/xmake/core/sandbox/modules/import/core/base/scheduler.lua index 222bee391..a3f9741c3 100644 --- a/xmake/core/sandbox/modules/import/core/base/scheduler.lua +++ b/xmake/core/sandbox/modules/import/core/base/scheduler.lua @@ -96,7 +96,11 @@ end -- resume the given coroutine function sandbox_core_base_scheduler.co_resume(co, ...) - return scheduler:resume(co:thread(), ...) + local ok, errors = scheduler:co_resume(co, ...) + if not ok then + raise(errors) + end + return ok, errors end -- suspend the current coroutine diff --git a/xmake/core/sandbox/modules/import/core/package/addon.lua b/xmake/core/sandbox/modules/import/core/package/addon.lua index 47d2b7793..9f080cc93 100644 --- a/xmake/core/sandbox/modules/import/core/package/addon.lua +++ b/xmake/core/sandbox/modules/import/core/package/addon.lua @@ -30,15 +30,11 @@ sandbox_core_package_addon.installdir = addon.installdir sandbox_core_package_addon.workdir = addon.workdir sandbox_core_package_addon.dirname = addon.dirname sandbox_core_package_addon.owner = addon.owner -sandbox_core_package_addon.is_reference = addon.is_reference -sandbox_core_package_addon.payloads = addon.payloads sandbox_core_package_addon.payloadinfos = addon.payloadinfos sandbox_core_package_addon.payloads_of = addon.payloads_of sandbox_core_package_addon.payloadroot = addon.payloadroot sandbox_core_package_addon.addons = addon.addons sandbox_core_package_addon.versions = addon.versions -sandbox_core_package_addon.pin = addon.pin -sandbox_core_package_addon.addondir = addon.addondir sandbox_core_package_addon.unregister = addon.unregister -- get the manifest of the given addon directory, e.g. <sourcedir>/addon.lua diff --git a/xmake/core/sandbox/modules/import/core/project/addons.lua b/xmake/core/sandbox/modules/import/core/project/addons.lua index 542cbaf4f..ff2fe14c0 100644 --- a/xmake/core/sandbox/modules/import/core/project/addons.lua +++ b/xmake/core/sandbox/modules/import/core/project/addons.lua @@ -23,30 +23,13 @@ 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.filename = addons.filename sandbox_core_project_addons.lockfile = addons.lockfile sandbox_core_project_addons.lockfile_version = addons.lockfile_version sandbox_core_project_addons.locked = addons.locked sandbox_core_project_addons.locked_valid = addons.locked_valid sandbox_core_project_addons.requirename = addons.requirename -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/core/sandbox/modules/import/lib/lua/error.lua b/xmake/core/sandbox/modules/import/lib/lua/error.lua new file mode 100644 index 000000000..4f38f1647 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/error.lua @@ -0,0 +1,36 @@ +--!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 error.lua +-- + +-- the native lua `error` +-- +-- it is not in the sandbox by default, `raise` is the one to use: it formats the +-- message and it is what `try`/`catch` understands. `error` is the plain one, for the +-- code which wants to throw a value rather than a message +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.error") +-- +-- error("something went wrong") +-- error({code = 1}) -- a value, not a message +-- +return error diff --git a/xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua b/xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua new file mode 100644 index 000000000..fcef76eb3 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua @@ -0,0 +1,32 @@ +--!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 getmetatable.lua +-- + +-- the native lua `getmetatable` +-- +-- @note the same in lua 5.1 and 5.4, it returns the `__metatable` field when the +-- metatable is protected +-- +-- e.g. +-- +-- import("lib.lua.getmetatable") +-- +-- local mt = getmetatable(obj) +-- +return getmetatable diff --git a/xmake/core/sandbox/modules/import/lib/lua/next.lua b/xmake/core/sandbox/modules/import/lib/lua/next.lua new file mode 100644 index 000000000..65313dd1a --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/next.lua @@ -0,0 +1,36 @@ +--!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 next.lua +-- + +-- the native lua `next` +-- +-- it is not in the sandbox by default, `pairs` covers the iteration of a table. `next` +-- is what is left for the two things `pairs` cannot say: whether a table is empty, and +-- stepping a traversal by hand +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.next") +-- +-- if next(tbl) == nil then ... end -- the table is empty +-- local key, value = next(tbl) -- the first entry, in no particular order +-- +return next diff --git a/xmake/core/sandbox/modules/import/lib/lua/pcall.lua b/xmake/core/sandbox/modules/import/lib/lua/pcall.lua new file mode 100644 index 000000000..1ad150943 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/pcall.lua @@ -0,0 +1,36 @@ +--!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 pcall.lua +-- + +-- the native lua `pcall` +-- +-- it is not in the sandbox by default, `try`/`catch` is the idiomatic way to handle the +-- errors of a script, and it keeps the traceback and the error message which xmake +-- builds. `pcall` is the plain one, for the code which only needs a boolean +-- +-- @note the same in lua 5.1 and 5.4. `xpcall` is not exported, its message handler +-- takes the extra arguments only since 5.2 +-- +-- e.g. +-- +-- import("lib.lua.pcall") +-- +-- local ok, result = pcall(function () return 1 end) +-- +return pcall diff --git a/xmake/core/sandbox/modules/import/lib/lua/rawequal.lua b/xmake/core/sandbox/modules/import/lib/lua/rawequal.lua new file mode 100644 index 000000000..b9f9ea683 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/rawequal.lua @@ -0,0 +1,33 @@ +--!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 rawequal.lua +-- + +-- the native lua `rawequal` +-- +-- it compares two values without going through the `__eq` metamethod +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.rawequal") +-- +-- if rawequal(a, b) then ... end +-- +return rawequal diff --git a/xmake/core/sandbox/modules/import/lib/lua/rawget.lua b/xmake/core/sandbox/modules/import/lib/lua/rawget.lua new file mode 100644 index 000000000..fe5115001 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/rawget.lua @@ -0,0 +1,34 @@ +--!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 rawget.lua +-- + +-- the native lua `rawget` +-- +-- it reads a field of a table without going through its `__index` metamethod, so it +-- sees what the table itself holds and not what its metatable would answer +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.rawget") +-- +-- local value = rawget(tbl, "key") +-- +return rawget diff --git a/xmake/core/sandbox/modules/import/lib/lua/rawset.lua b/xmake/core/sandbox/modules/import/lib/lua/rawset.lua new file mode 100644 index 000000000..917bc3d86 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/rawset.lua @@ -0,0 +1,33 @@ +--!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 rawset.lua +-- + +-- the native lua `rawset` +-- +-- it writes a field of a table without going through its `__newindex` metamethod +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.rawset") +-- +-- rawset(tbl, "key", value) +-- +return rawset diff --git a/xmake/core/sandbox/modules/import/lib/lua/select.lua b/xmake/core/sandbox/modules/import/lib/lua/select.lua new file mode 100644 index 000000000..45fb1bbc9 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/select.lua @@ -0,0 +1,36 @@ +--!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 select.lua +-- + +-- the native lua `select` +-- +-- it is not in the sandbox by default, the scripts of a project rarely need to look at +-- a raw argument list, but it is the only way to count the arguments of a vararg +-- function without losing the nils in it +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.select") +-- +-- local count = select("#", ...) -- how many arguments, nils included +-- local second = select(2, ...) -- everything from the second one on +-- +return select diff --git a/xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua b/xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua new file mode 100644 index 000000000..6f1d4c330 --- /dev/null +++ b/xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua @@ -0,0 +1,36 @@ +--!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 setmetatable.lua +-- + +-- the native lua `setmetatable` +-- +-- it is not in the sandbox by default: a module which builds objects with metatables is +-- a module which is hard to serialize and to cache, and xmake passes plain tables +-- around on purpose. it is here for the code which really wants operator overloading or +-- an `__index` fallback +-- +-- @note the same in lua 5.1 and 5.4 +-- +-- e.g. +-- +-- import("lib.lua.setmetatable") +-- +-- local obj = setmetatable({}, {__index = function (tbl, key) return "?" end}) +-- +return setmetatable diff --git a/xmake/languages/c/load.lua b/xmake/languages/c/load.lua index a265f0dbd..7497cc004 100644 --- a/xmake/languages/c/load.lua +++ b/xmake/languages/c/load.lua @@ -61,6 +61,7 @@ function _get_apis() , "package.add_defines" , "package.add_undefines" , "package.add_frameworks" + , "package.add_vectorexts" , "package.add_rpathdirs" , "package.add_linkdirs" , "package.add_includedirs" --@note we need not uses paths for package, see https://github.com/xmake-io/xmake/issues/717 diff --git a/xmake/modules/detect/sdks/find_mingw.lua b/xmake/modules/detect/sdks/find_mingw.lua index c70bebb11..d6d7e0d8e 100644 --- a/xmake/modules/detect/sdks/find_mingw.lua +++ b/xmake/modules/detect/sdks/find_mingw.lua @@ -108,7 +108,8 @@ function _find_mingw(sdkdir, opt) -- find cross toolchain local toolchain = find_cross_toolchain(sdkdir or bindir, {bindir = bindir, cross = cross}) - if not toolchain then -- fallback, e.g. gcc.exe without cross + -- fallback, e.g. gcc.exe without cross + if not toolchain and (is_host("windows") or is_subhost("msys", "cygwin")) then toolchain = find_cross_toolchain(sdkdir or bindir, {bindir = bindir}) end if toolchain then diff --git a/xmake/modules/package/tools/xmake.lua b/xmake/modules/package/tools/xmake.lua index 4efee0974..305074df0 100644 --- a/xmake/modules/package/tools/xmake.lua +++ b/xmake/modules/package/tools/xmake.lua @@ -283,6 +283,14 @@ function _get_configs(package, configs, opt) if not package:use_external_includes() and (not policies or not policies:find("package.include_external_headers", 1, true)) then table.insert(policies_list, "package.include_external_headers:n") end + -- the sub-process must install its packages locally too, otherwise they go to the + -- global directory while we expect them under our build directory, + -- @see https://github.com/xmake-io/xmake/issues/7716 + for _, policyname in ipairs({"package.install_locally", "package.host.install_locally"}) do + if project.policy(policyname) and (not policies or not policies:find(policyname, 1, true)) then + table.insert(policies_list, policyname) + end + end if policies and policies:find("package.build.ccache", 1, true) then table.insert(configs, "--ccachedir=" .. path.join(path.directory(package:cachedir()), "build_cache")) table.insert(policies_list, "build.ccache") @@ -527,14 +535,19 @@ function install(package, configs, opt) -- get build environments local envs = opt.envs or buildenvs(package) - -- if the package is installed locally, pass the local packages directory - -- to the child xmake process so it can find already-installed deps - -- without re-installing them to the global directory + -- if the package is installed locally, pass our local packages directory to the + -- child xmake process, so the packages it installs locally land in the same place + -- and the deps we have already installed are found instead of installed again + -- + -- @note we must not override `XMAKE_PKG_INSTALLDIR` here: it is the *global* root + -- of the child, and overriding it hides `~/.xmake/packages` from it, so the host + -- packages it needs (e.g. the toolchains) would be installed again under our + -- build directory, @see https://github.com/xmake-io/xmake/issues/7716 + -- -- @see https://github.com/xmake-io/xmake/discussions/7441 if package:is_local() and not package:is_source_embed() then envs = table.clone(envs) - envs.XMAKE_PKG_INSTALLDIR = package_core.installdir({localdir = true}) - envs.XMAKE_PKG_CACHEDIR = package_core.cachedir({localdir = true}) + envs.XMAKE_PKG_LOCALDIR = package_core.installdir({localdir = true}) end -- pass local repositories diff --git a/xmake/modules/private/action/addon/impl/install_addons.lua b/xmake/modules/private/action/addon/impl/install_addons.lua index 0a288f1a3..4cc5b0fba 100644 --- a/xmake/modules/private/action/addon/impl/install_addons.lua +++ b/xmake/modules/private/action/addon/impl/install_addons.lua @@ -28,9 +28,9 @@ import("private.action.addon.impl.xrepo", {alias = "xrepo_addon"}) -- @note we install the locked versions, but the declaration is authoritative, so we -- resolve them again if the user has changed it or upgrades them -- -function _get_requires(addonsinfo, locked) +function _get_requires(declared, locked) local requires = {} - for _, requirestr in ipairs(addonsinfo.addons) do + for _, requirestr in ipairs(declared) do local name = addons.requirename(requirestr) local lockinfo = locked and locked[name] if addons.locked_valid(requirestr, lockinfo) then @@ -46,7 +46,7 @@ end -- @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) +function _lock_addons(projectdir, declared) local lockinfo = {} local locked = addons.locked(projectdir) or {} @@ -54,7 +54,7 @@ function _lock_addons(projectdir, addonsinfo) -- and we must not see them through the locked versions, we are locking them right now, -- e.g. `xmake addon --upgrade` pins the old ones before loading this project local installed = addon.addons({force = true, unpinned = true}) - for _, requirestr in ipairs(addonsinfo.addons) do + for _, requirestr in ipairs(declared) do local name = addons.requirename(requirestr) local addoninfo = assert(installed[addon.dirname(name)], "addon(%s) is not installed!", name) local oldversion = locked[name] and locked[name].version @@ -79,41 +79,49 @@ function _lock_addons(projectdir, addonsinfo) os.rm(tmpfile) end --- install the addons which the given project declares in its `xmake-addons.lua` +-- install the addons which a project declares, e.g. add_addons("esp32-devel 1.0.x") -- -- @note we are also called from a sub-process, the project cannot be loaded until -- its addons are installed, @see core/project/project.lua -- -function main(projectdir, opt) +-- install the addons which a project declares, e.g. add_addons("esp32-devel 1.0.x") +-- +-- @param projectdir the project directory, we only read/write its lock file here +-- @param datafile the declarations of the project, {addons = {...}, repositories = {...}} +-- +-- @note we are always run in a working directory which has no project, we cannot load +-- the project again here, @see xmake/core/project/project.lua +-- +function main(projectdir, datafile, opt) opt = opt or {} projectdir = projectdir or os.projectdir() - local addonsinfo = addons.load(projectdir) - if not addonsinfo or #addonsinfo.addons == 0 then + local declarations = type(datafile) == "table" and datafile or io.load(datafile) + local declared = table.wrap(declarations and declarations.addons) + if #declared == 0 then return end -- upgrade them? we need to resolve the declared versions again local locked = not opt.upgrade and addons.locked(projectdir) or nil - -- install them with xrepo, it installs the packages in its own working directory, - -- so we need not a project here - -- this project declares its own repositories? we pass them to xrepo, -- they are only used by this installation, we do not register them globally local rcfile - if #addonsinfo.repositories > 0 then + local repositories = table.wrap(declarations.repositories) + if #repositories > 0 then rcfile = os.tmpfile() .. ".lua" local file = io.open(rcfile, "w") - for _, repo in ipairs(addonsinfo.repositories) do + for _, repo in ipairs(repositories) do file:print("add_repositories(%q)", repo) end file:close() end + -- install them with xrepo, it installs the packages in its own working directory try { function () - xrepo_addon("install", _get_requires(addonsinfo, locked), {includes = rcfile}) + xrepo_addon("install", _get_requires(declared, locked), {includes = rcfile}) end, finally { @@ -130,5 +138,5 @@ function main(projectdir, opt) } -- and lock them, so that the other users get the same versions - _lock_addons(projectdir, addonsinfo) + _lock_addons(projectdir, declared) end diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index bd673fa0c..3b4fe8296 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -695,7 +695,7 @@ function get_zig_target(toolchain) end if toolchain:is_plat("cross") then - -- xmake f -p cross --toolchain=zig --cross=mips64el-linux-gnuabi64 + -- xmake f -p cross --toolchain=zigcc --cross=mips64el-linux-gnuabi64 elseif toolchain:is_plat("macosx") then --@see https://github.com/ziglang/zig/issues/14226 target = arch .. "-macos-none" diff --git a/xmake/platforms/bsd/xmake.lua b/xmake/platforms/bsd/xmake.lua index 98143d019..89ab580fe 100644 --- a/xmake/platforms/bsd/xmake.lua +++ b/xmake/platforms/bsd/xmake.lua @@ -21,7 +21,7 @@ platform("bsd") set_os("bsd") set_hosts("bsd") - set_archs("i386", "x86_64") + set_archs("i386", "x86_64", "arm", "arm64", "ppc", "ppc64", "ppc64el", "riscv64", "sparc64") set_formats("static", "lib$(name).a") set_formats("object", "$(name).o") @@ -53,5 +53,3 @@ platform("bsd") , {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" } } } - - diff --git a/xmake/platforms/cross/xmake.lua b/xmake/platforms/cross/xmake.lua index 98bb31634..462fc961b 100644 --- a/xmake/platforms/cross/xmake.lua +++ b/xmake/platforms/cross/xmake.lua @@ -20,7 +20,7 @@ platform("cross") set_hosts("macosx", "linux", "windows", "bsd") - set_archs("i386", "x86_64", "arm", "arm64", "mips", "mips64", "riscv", "riscv64", "loong64", "s390x", "ppc", "ppc64", "sh4") + set_archs("i386", "x86_64", "arm", "armv7", "arm64", "mips", "mips64", "mips64el", "riscv", "riscv64", "loong64", "s390x", "ppc", "ppc64", "ppc64el", "sh4", "sparc64") set_formats("static", "lib$(name).a") set_formats("object", "$(name).o") @@ -28,5 +28,3 @@ platform("cross") set_formats("symbol", "$(name).sym") set_toolchains("envs", "cross") - - diff --git a/xmake/platforms/linux/xmake.lua b/xmake/platforms/linux/xmake.lua index c4d09880e..c26f99fb5 100644 --- a/xmake/platforms/linux/xmake.lua +++ b/xmake/platforms/linux/xmake.lua @@ -21,7 +21,7 @@ platform("linux") set_os("linux") set_hosts("macosx", "linux", "windows", "bsd") - set_archs("i386", "x86_64", "armv7", "armv7s", "arm64", "mips", "mips64", "mipsel", "mips64el", "loong64") + set_archs("i386", "x86_64", "armv7", "armv7s", "arm64", "mips", "mips64", "mipsel", "mips64el", "loong64", "riscv64", "s390x", "ppc", "ppc64", "ppc64el", "sparc64") set_formats("static", "lib$(name).a") set_formats("object", "$(name).o") diff --git a/xmake/platforms/solaris/xmake.lua b/xmake/platforms/solaris/xmake.lua index 5abdde4fb..260bc138b 100644 --- a/xmake/platforms/solaris/xmake.lua +++ b/xmake/platforms/solaris/xmake.lua @@ -21,7 +21,7 @@ platform("solaris") set_os("solaris") set_hosts("solaris") - set_archs("i386", "x86_64") + set_archs("i386", "x86_64", "sparc64") set_formats("static", "lib$(name).a") set_formats("object", "$(name).o") @@ -53,5 +53,3 @@ platform("solaris") , {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" } } } - - diff --git a/xmake/plugins/doxygen/main.lua b/xmake/plugins/doxygen/main.lua index 80fd77675..b84754cb2 100644 --- a/xmake/plugins/doxygen/main.lua +++ b/xmake/plugins/doxygen/main.lua @@ -71,6 +71,10 @@ end function main() + -- @note we cannot use utils.warning() here, it's queued and only shown at the end + cprint("${bright color.warning}${text.warning}: ${color.warning}the builtin `xmake doxygen` plugin is deprecated, " .. + "please use the doxygen-plugin addon: `xmake addon --install doxygen-plugin`") + -- load configuration config.load() diff --git a/xmake/plugins/format/main.lua b/xmake/plugins/format/main.lua index 408de0421..c8f50a15e 100644 --- a/xmake/plugins/format/main.lua +++ b/xmake/plugins/format/main.lua @@ -103,6 +103,10 @@ end -- main function main() + -- @note we cannot use utils.warning() here, it's queued and only shown at the end + cprint("${bright color.warning}${text.warning}: ${color.warning}the builtin `xmake format` plugin is deprecated, " .. + "please use the format-plugin addon: `xmake addon --install format-plugin`") + -- load configuration config.load() diff --git a/xmake/plugins/macro/main.lua b/xmake/plugins/macro/main.lua index 86b50e2da..6c2c8c734 100644 --- a/xmake/plugins/macro/main.lua +++ b/xmake/plugins/macro/main.lua @@ -309,6 +309,10 @@ end -- main function main() + -- @note we cannot use utils.warning() here, it's queued and only shown at the end + cprint("${bright color.warning}${text.warning}: ${color.warning}the builtin `xmake macro` plugin is deprecated, " .. + "please use the macro-plugin addon: `xmake addon --install macro-plugin`") + -- list macros if option.get("list") then diff --git a/xmake/plugins/project/clang/compile_commands.lua b/xmake/plugins/project/clang/compile_commands.lua index 2f25194cc..a8fdb547f 100644 --- a/xmake/plugins/project/clang/compile_commands.lua +++ b/xmake/plugins/project/clang/compile_commands.lua @@ -69,10 +69,13 @@ end -- specify windows sdk verison function _get_windows_sdk_arguments(target) local args = {} - local msvc = target:toolchain("msvc") - if msvc then - local envs = msvc:runenvs() - if envs then + if target and target:is_plat("windows") then + local toolchain = target:toolchain("msvc") or + target:toolchain("clang-cl") or + target:toolchain("clang") or + target:toolchain("llvm") + local envs = toolchain and toolchain:runenvs() + if envs and envs.INCLUDE then for _, dir in ipairs(path.splitenv(envs.INCLUDE)) do table.insert(args, "-imsvc") table.insert(args, dir) diff --git a/xmake/rules/c++/modules/clang/scanner.lua b/xmake/rules/c++/modules/clang/scanner.lua index d73c40621..f0023cb7c 100644 --- a/xmake/rules/c++/modules/clang/scanner.lua +++ b/xmake/rules/c++/modules/clang/scanner.lua @@ -59,7 +59,8 @@ function scan_dependency_for(target, sourcefile, rescan, opt) if option.get("verbose") then print(os.args(table.join(clangscandeps, dependency_flags))) end - local outdata, errdata = os.iorunv(clangscandeps, dependency_flags) + local outdata, errdata = os.iorunv(clangscandeps, dependency_flags, + {envs = compinst:runenvs()}) assert(outdata, errdata) io.writefile(jsonfile, outdata) @@ -77,7 +78,7 @@ function scan_dependency_for(target, sourcefile, rescan, opt) end) local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i")) compflags = table.join(compflags or {}, keepsystemincludesflag or {}, {"-E", "-x", "c++", file, "-o", ifile}) - os.vrunv(compinst:program(), compflags) + os.vrunv(compinst:program(), compflags, {envs = compinst:runenvs()}) local content = io.readfile(ifile) os.rm(ifile) return content diff --git a/xmake/rules/c++/modules/clang/support.lua b/xmake/rules/c++/modules/clang/support.lua index b1699d21a..71bd2e735 100644 --- a/xmake/rules/c++/modules/clang/support.lua +++ b/xmake/rules/c++/modules/clang/support.lua @@ -46,7 +46,10 @@ function _get_toolchain_includedirs_for_stlheaders(target, includedirs, clang) table.insert(argv, 1, "-stdlib=libstdc++") end end - local result = try {function () return os.iorunv(clang, argv, {envs = compinst:runenvs()}) end} + local compinst = target:compiler("cxx") + local result = try {function () + return os.iorunv(clang, argv, {envs = compinst and compinst:runenvs()}) + end} if result then for _, line in ipairs(result:split("\n", {plain = true})) do local line = line:trim() @@ -64,17 +67,25 @@ end function _get_std_module_manifest_path(target) local print_module_manifest_flag = get_print_library_module_manifest_path_flag(target) - local clang_path = path.directory(get_clang_path(target)) + local clang_path = get_clang_path(target) + if not clang_path then + return + end + local clang_dir = path.directory(clang_path) if print_module_manifest_flag then local compinst = target:compiler("cxx") - local outdata, _ = try { function() return os.iorunv(compinst:program(), {"-std=c++23", "-stdlib=libc++", "--sysroot=" .. path.join(clang_path, ".."), print_module_manifest_flag}, {envs = compinst:runenvs()}) end } + local sysroot = "--sysroot=" .. path.join(clang_dir, "..") + local flags = {"-std=c++23", "-stdlib=libc++", sysroot, print_module_manifest_flag} + local outdata, _ = try {function() + return os.iorunv(compinst:program(), flags, {envs = compinst:runenvs()}) + end} if outdata and not outdata:startswith("<NOT PRESENT>") then return outdata:trim() end end -- fallback on custom detection -- manifest can be found in <llvm_path>/lib subdirectory (i.e on debian it should be <llvm_path>/lib/x86_64-unknown-linux-gnu/) - local clang_lib_path = path.join(clang_path, "..", "lib") + local clang_lib_path = path.join(clang_dir, "..", "lib") local modules_json_path = path.join(clang_lib_path, "libc++.modules.json") if not os.isfile(modules_json_path) then modules_json_path = find_file("*/libc++.modules.json", clang_lib_path) @@ -161,7 +172,11 @@ function toolchain_includedirs(target) runtime_flag = "-stdlib=libstdc++" end end - local _, result = try {function () return os.iorunv(clang, table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {})) end} + local compinst = target:compiler("cxx") + local flags = table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {}) + local _, result = try {function () + return os.iorunv(clang, flags, {envs = compinst and compinst:runenvs()}) + end} if result then for _, line in ipairs(result:split("\n", {plain = true})) do local line = line:trim() @@ -183,8 +198,10 @@ function get_clang_path(target) if not clang_path then local program, toolname = target:tool("cxx") if program and toolname:startswith("clang") then + local compinst = target:compiler("cxx") + local envs = compinst and compinst:runenvs() or os.getenvs() local clang = find_tool(toolname, {program = program, - envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname}) + envs = envs, cachekey = "modules_support_clang_" .. toolname}) if clang then clang_path = clang.program end @@ -201,8 +218,10 @@ function get_clang_version(target) if not clang_version then local program, toolname = target:tool("cxx") if program and toolname:startswith("clang") then + local compinst = target:compiler("cxx") + local envs = compinst and compinst:runenvs() or os.getenvs() local clang = find_tool(toolname, {program = program, version = true, - envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname}) + envs = envs, cachekey = "modules_support_clang_" .. toolname}) if clang then clang_version = clang.version end @@ -229,10 +248,13 @@ function get_clang_scan_deps(target) if dir and dir ~= "." and os.isdir(dir) then program = path.join(dir, program) end - local result = find_tool("clang-scan-deps", {program = program, version = true}) + local compinst = target:compiler("cxx") + local envs = compinst and compinst:runenvs() or os.getenvs() + local result = find_tool("clang-scan-deps", + {program = program, version = true, envs = envs}) if not result then -- find a system wide alternative - result = find_tool("clang-scan-deps", {version = true}) + result = find_tool("clang-scan-deps", {version = true, envs = envs}) end if result then clang_scan_deps = result.program @@ -295,9 +317,16 @@ function get_stdmodules(target) return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))} end -- then try the directory relative to clang bin directory - try_std_module_directory = path.join(path.directory(get_original_file(get_clang_path(target))), std_module_directory) - if os.isdir(try_std_module_directory) then - return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))} + local clang_path = get_clang_path(target) + if clang_path then + local clang_dir = path.directory(get_original_file(clang_path)) + try_std_module_directory = path.join(clang_dir, std_module_directory) + if os.isdir(try_std_module_directory) then + return { + path.normalize(path.join(try_std_module_directory, "std.cppm")), + path.normalize(path.join(try_std_module_directory, "std.compat.cppm")) + } + end end elseif cpplib == "stdc++" then -- dont be greedy and don't enable stdc++ std module support for llvm < 19 diff --git a/xmake/rules/c++/modules/support.lua b/xmake/rules/c++/modules/support.lua index 126e25c61..38df28a05 100644 --- a/xmake/rules/c++/modules/support.lua +++ b/xmake/rules/c++/modules/support.lua @@ -92,7 +92,15 @@ function get_cpplibrary_name(target) end elseif target:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros", "bsd", "harmony") then return "c++" - elseif target:is_plat("linux", "mingw", "cygwin", "msys", "haiku") then + elseif target:is_plat("linux", "cygwin", "msys", "haiku") then + return "stdc++" + elseif target:is_plat("mingw") then + local toolchain_inst = target:toolchain("mingw") + local is_clang = (toolchain_inst and toolchain_inst:config("clang")) or + target:has_tool("cxx", "clang", "clangxx", "clang_cl") + if is_clang then + return "c++" + end return "stdc++" elseif target:is_plat("windows") then return "msstl" diff --git a/xmake/toolchains/mingw/xmake.lua b/xmake/toolchains/mingw/xmake.lua index d0695dcfe..a75ab3cce 100644 --- a/xmake/toolchains/mingw/xmake.lua +++ b/xmake/toolchains/mingw/xmake.lua @@ -22,7 +22,7 @@ toolchain("mingw") set_kind("standalone") set_homepage("http://www.mingw.org/") set_description("Minimalist GNU for Windows") - set_runtimes("stdc++_static", "stdc++_shared") + set_runtimes("stdc++_static", "stdc++_shared", "c++_static", "c++_shared") on_check("check") on_load(function (toolchain) |
