summaryrefslogtreecommitdiff
path: root/xmake/modules
diff options
context:
space:
mode:
Diffstat (limited to 'xmake/modules')
-rw-r--r--xmake/modules/core/project/depend.lua3
-rw-r--r--xmake/modules/core/tools/clang.lua45
-rw-r--r--xmake/modules/core/tools/ml.lua20
-rw-r--r--xmake/modules/detect/sdks/find_mingw.lua3
-rw-r--r--xmake/modules/detect/sdks/find_ndk.lua2
-rw-r--r--xmake/modules/detect/tools/find_emar.lua21
-rw-r--r--xmake/modules/detect/tools/find_emcc.lua19
-rw-r--r--xmake/modules/detect/tools/find_emrun.lua21
-rw-r--r--xmake/modules/detect/tools/find_emxx.lua19
-rw-r--r--xmake/modules/net/http/download.lua73
-rw-r--r--xmake/modules/package/manager/install_package.lua2
-rw-r--r--xmake/modules/package/manager/pkg/install_package.lua62
-rw-r--r--xmake/modules/package/manager/vcpkg/find_package.lua36
-rw-r--r--xmake/modules/package/manager/vcpkg/install_package.lua5
-rw-r--r--xmake/modules/package/manager/vcpkg/utils.lua18
-rw-r--r--xmake/modules/package/manager/xmake/search_package.lua4
-rw-r--r--xmake/modules/package/tools/cmake.lua34
-rw-r--r--xmake/modules/package/tools/xmake.lua23
-rw-r--r--xmake/modules/private/action/addon/impl/install_addons.lua142
-rw-r--r--xmake/modules/private/action/addon/impl/xrepo.lua57
-rw-r--r--xmake/modules/private/action/build/pcheader.lua16
-rw-r--r--xmake/modules/private/action/require/impl/actions/download.lua1
-rw-r--r--xmake/modules/private/action/require/impl/actions/download_resources.lua1
-rw-r--r--xmake/modules/private/action/require/impl/actions/install.lua57
-rw-r--r--xmake/modules/private/action/require/impl/actions/patch_sources.lua1
-rw-r--r--xmake/modules/private/action/require/impl/install_packages.lua25
-rw-r--r--xmake/modules/private/action/require/impl/package.lua148
-rw-r--r--xmake/modules/private/action/require/impl/repository.lua8
-rw-r--r--xmake/modules/private/action/require/install.lua9
-rw-r--r--xmake/modules/private/action/require/search.lua8
-rw-r--r--xmake/modules/private/check/checkers/api/package/kind.lua4
-rw-r--r--xmake/modules/private/utils/batchcmds.lua30
-rw-r--r--xmake/modules/private/utils/toolchain.lua54
-rw-r--r--xmake/modules/private/xrepo/action/install.lua9
-rw-r--r--xmake/modules/private/xrepo/action/remove.lua18
-rw-r--r--xmake/modules/private/xrepo/action/search.lua6
-rw-r--r--xmake/modules/private/xrepo/quick_search/cache.lua68
37 files changed, 970 insertions, 102 deletions
diff --git a/xmake/modules/core/project/depend.lua b/xmake/modules/core/project/depend.lua
index 511e6fde6..40d6ba757 100644
--- a/xmake/modules/core/project/depend.lua
+++ b/xmake/modules/core/project/depend.lua
@@ -167,6 +167,9 @@ function is_changed(dependinfo, opt)
end
return true
elseif deptype == "table" then
+ if #depvalue ~= #optvalue then
+ return true
+ end
for subidx, subvalue in ipairs(depvalue) do
if subvalue ~= optvalue[subidx] then
if _is_show_diagnosis_info() then
diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua
index 1250d1a46..c264f6656 100644
--- a/xmake/modules/core/tools/clang.lua
+++ b/xmake/modules/core/tools/clang.lua
@@ -192,6 +192,19 @@ function _has_static_libstdcxx(self)
return has_static_libstdcxx
end
+-- has -nostdlib++? (disable the automatic c++ runtime link, so we can link libc++/libc++abi explicitly)
+function _has_nostdlibxx(self)
+ local has_nostdlibxx = _g._HAS_NOSTDLIBXX
+ if has_nostdlibxx == nil then
+ if self:has_flags("-nostdlib++ -Werror", "ldflags", {flagskey = "clang_nostdlibxx"}) then
+ has_nostdlibxx = true
+ end
+ has_nostdlibxx = has_nostdlibxx or false
+ _g._HAS_NOSTDLIBXX = has_nostdlibxx
+ end
+ return has_nostdlibxx
+end
+
-- make the runtime flag
-- @see https://github.com/xmake-io/xmake/issues/3546
function nf_runtime(self, runtime, opt)
@@ -284,8 +297,36 @@ function nf_runtime(self, runtime, opt)
end
end
end
- if runtime:endswith("_static") and _has_static_libstdcxx(self) then
- maps["stdc++_static"] = table.join(maps["stdc++_static"], "-static-libstdc++")
+ if runtime:endswith("_static") then
+ -- -static-libstdc++ only pulls in libc++.a, not libc++abi.a, so the libc++abi
+ -- symbols (typeinfo, __cxa_*) stay undefined and the link fails, especially
+ -- once c++ modules reference more of libc++.
+ --
+ -- if we can locate both static archives, link them explicitly in a group
+ -- (they reference each other) and disable the driver's automatic c++ runtime
+ -- with -nostdlib++, otherwise (bundled abi) fall back to -static-libstdc++.
+ --
+ -- note: target.runtimes is ordered right before the syslinks in the c++ link
+ -- order, so these archives are placed after the object files / user links.
+ -- @see https://github.com/xmake-io/xmake/issues/7442, https://github.com/xmake-io/xmake/issues/7656
+ if llvm_dirs.libcxx_static and llvm_dirs.libcxxabi_static and _has_nostdlibxx(self) then
+ local cxxlibs = {llvm_dirs.libcxx_static, llvm_dirs.libcxxabi_static}
+ -- apple ld64 does not support --start-group, it always rescans archives
+ if not self:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros") then
+ cxxlibs = table.join("-Wl,--start-group", cxxlibs, "-Wl,--end-group")
+ end
+ -- -stdlib=libc++ is unused when linking with -nostdlib++, remove it to avoid
+ -- the `argument unused during compilation` warning
+ local ldflags = table.remove_if(table.wrap(maps["c++_static"]), function (_, flag)
+ return flag == "-stdlib=libc++"
+ end)
+ maps["c++_static"] = table.join(ldflags, "-nostdlib++", cxxlibs)
+ elseif _has_static_libstdcxx(self) then
+ maps["c++_static"] = table.join(maps["c++_static"], "-static-libstdc++")
+ end
+ if _has_static_libstdcxx(self) then
+ maps["stdc++_static"] = table.join(maps["stdc++_static"], "-static-libstdc++")
+ end
end
end
end
diff --git a/xmake/modules/core/tools/ml.lua b/xmake/modules/core/tools/ml.lua
index 2e7628cd2..229a7303a 100644
--- a/xmake/modules/core/tools/ml.lua
+++ b/xmake/modules/core/tools/ml.lua
@@ -35,7 +35,7 @@ function init(self)
self:set("mapflags",
{
-- symbols
- ["-g"] = "-Z7"
+ ["-g"] = "-Zi"
, ["-fvisibility=.*"] = ""
-- warnings
@@ -55,20 +55,14 @@ function init(self)
end
-- make the symbol flags
+--
+-- masm only supports -Zi/-Zd, the -Z7 and -ZI flags of cl.exe are rejected (A4018).
+-- -Zi already embeds debug info in the object file (masm has no compile-time pdb),
+-- so the "embed" and "edit" levels degrade to it.
function nf_symbols(self, levels)
- local flags = nil
- local values = hashset.from(levels)
- if values:has("debug") then
- flags = {}
- if values:has("edit") then
- table.insert(flags, "-ZI")
- elseif values:has("embed") then
- table.insert(flags, "-Z7")
- else
- table.insert(flags, "-Zi")
- end
+ if hashset.from(levels):has("debug") then
+ return {"-Zi"}
end
- return flags
end
-- make the warning flag
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/detect/sdks/find_ndk.lua b/xmake/modules/detect/sdks/find_ndk.lua
index ce7a4b712..a89536d5f 100644
--- a/xmake/modules/detect/sdks/find_ndk.lua
+++ b/xmake/modules/detect/sdks/find_ndk.lua
@@ -95,7 +95,7 @@ function _find_ndk_sdkver(sdkdir, bindir, sysroot, arch)
-- try to select the best compatible version
local sdkver = "16"
if use_llvm or arch == "arm64-v8a" or arch == "riscv64" then
- sdkver = (arch == "riscv64") and "35" or "21"
+ sdkver = (arch == "riscv64") and "36" or "21"
end
if sysroot then
if os.isdir(path.join(sysroot, "usr", "lib", triple, sdkver)) then
diff --git a/xmake/modules/detect/tools/find_emar.lua b/xmake/modules/detect/tools/find_emar.lua
index 8a6ec2691..138646243 100644
--- a/xmake/modules/detect/tools/find_emar.lua
+++ b/xmake/modules/detect/tools/find_emar.lua
@@ -83,5 +83,24 @@ function main(opt)
end
-- find program
- return find_program(opt.program or (is_host("windows") and "emar.bat" or "emar"), opt)
+ -- emsdk 6.0.0+ ships .exe on windows, older releases ship .bat
+ local program
+ if opt.program then
+ program = find_program(opt.program, opt)
+ else
+ local candidate_names
+ if is_host("windows") then
+ candidate_names = {"emar.exe", "emar.bat"}
+ else
+ candidate_names = {"emar"}
+ end
+ for _, name in ipairs(candidate_names) do
+ program = find_program(name, opt)
+ if program then
+ break
+ end
+ end
+ end
+
+ return program
end
diff --git a/xmake/modules/detect/tools/find_emcc.lua b/xmake/modules/detect/tools/find_emcc.lua
index e44223d63..dc77b716e 100644
--- a/xmake/modules/detect/tools/find_emcc.lua
+++ b/xmake/modules/detect/tools/find_emcc.lua
@@ -50,7 +50,24 @@ function main(opt)
end
-- find program
- local program = find_program(opt.program or (is_host("windows") and "emcc.bat" or "emcc"), opt)
+ -- emsdk 6.0.0+ ships .exe on windows, older releases ship .bat
+ local program
+ if opt.program then
+ program = find_program(opt.program, opt)
+ else
+ local candidate_names
+ if is_host("windows") then
+ candidate_names = {"emcc.exe", "emcc.bat"}
+ else
+ candidate_names = {"emcc"}
+ end
+ for _, name in ipairs(candidate_names) do
+ program = find_program(name, opt)
+ if program then
+ break
+ end
+ end
+ end
-- find program version
local version = nil
diff --git a/xmake/modules/detect/tools/find_emrun.lua b/xmake/modules/detect/tools/find_emrun.lua
index ac210c4ca..6a96c9d82 100644
--- a/xmake/modules/detect/tools/find_emrun.lua
+++ b/xmake/modules/detect/tools/find_emrun.lua
@@ -51,9 +51,26 @@ function main(opt)
table.insert(paths, emsdk.emscripten)
opt.paths = paths
end
-
+
-- find program
- local program = find_program(opt.program or (is_host("windows") and "emrun.bat" or "emrun"), opt)
+ -- emsdk 6.0.0+ ships .exe on windows, older releases ship .bat
+ local program
+ if opt.program then
+ program = find_program(opt.program, opt)
+ else
+ local candidate_names
+ if is_host("windows") then
+ candidate_names = {"emrun.exe", "emrun.bat"}
+ else
+ candidate_names = {"emrun"}
+ end
+ for _, name in ipairs(candidate_names) do
+ program = find_program(name, opt)
+ if program then
+ break
+ end
+ end
+ end
-- find program version
local version = nil
diff --git a/xmake/modules/detect/tools/find_emxx.lua b/xmake/modules/detect/tools/find_emxx.lua
index 4f8cc90ed..6ba454d1d 100644
--- a/xmake/modules/detect/tools/find_emxx.lua
+++ b/xmake/modules/detect/tools/find_emxx.lua
@@ -50,7 +50,24 @@ function main(opt)
end
-- find program
- local program = find_program(opt.program or (is_host("windows") and "em++.bat" or "em++"), opt)
+ -- emsdk 6.0.0+ ships .exe on windows, older releases ship .bat
+ local program
+ if opt.program then
+ program = find_program(opt.program, opt)
+ else
+ local candidate_names
+ if is_host("windows") then
+ candidate_names = {"em++.exe", "em++.bat"}
+ else
+ candidate_names = {"em++"}
+ end
+ for _, name in ipairs(candidate_names) do
+ program = find_program(name, opt)
+ if program then
+ break
+ end
+ end
+ end
-- find program version
local version = nil
diff --git a/xmake/modules/net/http/download.lua b/xmake/modules/net/http/download.lua
index 2695be817..e171c152d 100644
--- a/xmake/modules/net/http/download.lua
+++ b/xmake/modules/net/http/download.lua
@@ -297,18 +297,8 @@ function _powershell_download(tool, url, outputfile, opt)
os.vrunv(tool.program, argv)
end
--- download url
---
--- @param url the input url
--- @param outputfile the output file
--- @param opt the option, {continue = true}
---
---
-function main(url, outputfile, opt)
-
- -- init output file
- opt = opt or {}
- outputfile = outputfile or path.filename(url):gsub("%?.+$", "")
+-- download url with the first available tool (aria2/curl/wget/powershell)
+function _download(url, outputfile, opt)
-- attempt to download url using aria2 first (multi-threaded, fastest)
local tool = find_tool("aria2", {version = true})
@@ -338,3 +328,62 @@ function main(url, outputfile, opt)
assert(tool, "aria2, curl or wget not found!")
end
+
+-- is it a ssl/tls certificate verification error?
+function _is_ssl_cert_error(errors)
+ errors = (errors or ""):lower()
+ return errors:find("ssl", 1, true)
+ or errors:find("tls", 1, true)
+ or errors:find("certificate", 1, true)
+ or errors:find("handshake", 1, true)
+end
+
+-- download url, and retry once with ssl verification disabled on a certificate error
+function _download_fallback(url, outputfile, opt)
+ local errors
+ local ok = try
+ {
+ function ()
+ _download(url, outputfile, opt)
+ return true
+ end,
+ catch
+ {
+ function (errs)
+ errors = tostring(errs)
+ end
+ }
+ }
+ if not ok then
+ if _is_ssl_cert_error(errors) then
+ wprint("download failed due to ssl certificate verification, retrying with ssl verification disabled ..")
+ return _download(url, outputfile, table.join(opt, {insecure = true}))
+ end
+ raise(errors)
+ end
+end
+
+-- download url
+--
+-- @param url the input url
+-- @param outputfile the output file
+-- @param opt the option, e.g. {continue = true, insecure = false, insecure_fallback = false}
+--
+-- @note if opt.insecure_fallback is enabled and the download fails due to a ssl certificate
+-- error, it will retry once with ssl verification disabled. this is only safe when the
+-- caller verifies the downloaded file afterwards (e.g. by its sha256 checksum).
+--
+function main(url, outputfile, opt)
+
+ -- init output file
+ opt = opt or {}
+ outputfile = outputfile or path.filename(url):gsub("%?.+$", "")
+
+ -- download it directly if we do not need the insecure fallback
+ if opt.insecure or not opt.insecure_fallback then
+ return _download(url, outputfile, opt)
+ end
+
+ -- download it with the insecure fallback
+ return _download_fallback(url, outputfile, opt)
+end
diff --git a/xmake/modules/package/manager/install_package.lua b/xmake/modules/package/manager/install_package.lua
index a2c605ba7..55458ca83 100644
--- a/xmake/modules/package/manager/install_package.lua
+++ b/xmake/modules/package/manager/install_package.lua
@@ -46,6 +46,8 @@ function _install_package(manager_name, package_name, opt)
table.insert(managers, "vcpkg")
table.insert(managers, "brew")
table.insert(managers, "nix")
+ elseif is_host("bsd") then
+ table.insert(managers, "pkg") -- freebsd/dragonflybsd
end
assert(#managers > 0, "no suitable package manager!")
diff --git a/xmake/modules/package/manager/pkg/install_package.lua b/xmake/modules/package/manager/pkg/install_package.lua
new file mode 100644
index 000000000..8d04f92b3
--- /dev/null
+++ b/xmake/modules/package/manager/pkg/install_package.lua
@@ -0,0 +1,62 @@
+--!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 install_package.lua
+--
+
+-- imports
+import("core.base.option")
+import("lib.detect.find_tool")
+import("privilege.sudo")
+
+-- install package
+--
+-- @param name the package name
+-- @param opt the options, e.g. {verbose = true, pkg = "the package name"}
+--
+-- @return true or false
+--
+function main(name, opt)
+
+ -- init options
+ opt = opt or {}
+
+ -- find pkg, e.g. freebsd, dragonflybsd
+ local pkg = find_tool("pkg")
+ if not pkg then
+ raise("pkg not found!")
+ end
+
+ -- init argv
+ local argv = {"install", "-y", opt.pkg or name}
+
+ -- install package directly if the current user is root
+ if os.isroot() then
+ os.vrunv(pkg.program, argv)
+ -- install with administrator permission?
+ elseif sudo.has() then
+
+ -- install it if be confirmed
+ local description = format("try installing %s with administrator permission", name)
+ local confirm = utils.confirm({default = true, description = description})
+ if confirm then
+ sudo.vrunv(pkg.program, argv)
+ end
+ else
+ raise("cannot get administrator permission!")
+ end
+end
diff --git a/xmake/modules/package/manager/vcpkg/find_package.lua b/xmake/modules/package/manager/vcpkg/find_package.lua
index 5b515d8a2..0504070a3 100644
--- a/xmake/modules/package/manager/vcpkg/find_package.lua
+++ b/xmake/modules/package/manager/vcpkg/find_package.lua
@@ -212,7 +212,6 @@ function _find_package(vcpkg, vcpkgdir, name, opt)
if required_features then
depend_name = name .. "[" .. table.concat(required_features, ",") .. "]"
end
- local result = nil
local argv = {"depend-info", depend_name, "--sort=reverse", "--triplet=" .. triplet}
-- pass feature flags to depend-info when in manifest mode, otherwise depend-info will not show the complete dependency tree with features
@@ -220,31 +219,44 @@ function _find_package(vcpkg, vcpkgdir, name, opt)
table.insert(argv, 1, "--feature-flags=versions")
end
- local _, dependinfo = try { function () return os.iorunv(vcpkg, argv, manifest_mode and {curdir = opt.installdir} or nil) end }
+ local _, dependinfo = try { function () return os.iorunv(vcpkg, argv, {curdir = manifest_mode and opt.installdir or vcpkg_utils.classic_curdir()}) end }
if manifest_mode and not dependinfo then
- -- fallback: newer vcpkg-tool no longer accepts the package name as a positional argument,
- -- drop it and retry. see https://github.com/microsoft/vcpkg-tool/pull/1909
+ -- fallback: in manifest mode vcpkg rejects the package name as a positional argument, so
+ -- drop it and query the manifest's dependency tree instead.
+ -- see https://github.com/microsoft/vcpkg-tool/pull/1909
table.remove(argv, 3)
_, dependinfo = try { function () return os.iorunv(vcpkg, argv, {curdir = opt.installdir}) end }
end
+
+ -- collect the packages to read info from: always the main package (its .list file was found
+ -- above, so it is known-installed), plus any transitive dependencies reported by depend-info.
+ -- this keeps resolution working even when depend-info fails, e.g. a project-owned vcpkg.json
+ -- puts vcpkg in manifest mode and it rejects arguments; only transitive deps are then missing.
+ -- @see https://github.com/xmake-io/xmake/issues/7660
+ local packagenames = {name}
if dependinfo then
for _, line in ipairs(dependinfo:split("\n", {plain = true})) do
if not line:startswith("vcpkg-") then
local packagename = line:match("^([^%[:]+)[^:]*:")
if packagename then
- local dependencyresult = _get_package_info(packagename, triplet, infodirs, arch, plat, mode)
- if dependencyresult then
- result = result or {}
- for key, dependencylist in pairs(dependencyresult) do
- result[key] = result[key] or {}
- table.join2(result[key], dependencylist)
- end
- end
+ table.insert(packagenames, packagename)
end
end
end
end
+ local result = nil
+ for _, packagename in ipairs(table.unique(packagenames)) do
+ local dependencyresult = _get_package_info(packagename, triplet, infodirs, arch, plat, mode)
+ if dependencyresult then
+ result = result or {}
+ for key, dependencylist in pairs(dependencyresult) do
+ result[key] = result[key] or {}
+ table.join2(result[key], dependencylist)
+ end
+ end
+ end
+
-- save version
if result then
local infoname = path.basename(infofile)
diff --git a/xmake/modules/package/manager/vcpkg/install_package.lua b/xmake/modules/package/manager/vcpkg/install_package.lua
index a8546e5d0..909374e61 100644
--- a/xmake/modules/package/manager/vcpkg/install_package.lua
+++ b/xmake/modules/package/manager/vcpkg/install_package.lua
@@ -64,7 +64,10 @@ function _install_for_classic(vcpkg, name, opt)
end
-- install package
- os.vrunv(vcpkg, argv)
+ -- run in a neutral directory so that a project-owned vcpkg.json in the current working
+ -- directory does not switch vcpkg into manifest mode (which rejects package arguments).
+ -- @see https://github.com/xmake-io/xmake/issues/7660
+ os.vrunv(vcpkg, argv, {curdir = vcpkg_utils.classic_curdir()})
end
-- install for manifest mode
diff --git a/xmake/modules/package/manager/vcpkg/utils.lua b/xmake/modules/package/manager/vcpkg/utils.lua
index fdd85d501..1c83323b4 100644
--- a/xmake/modules/package/manager/vcpkg/utils.lua
+++ b/xmake/modules/package/manager/vcpkg/utils.lua
@@ -36,6 +36,22 @@ function need_manifest(opt)
end
end
+-- get a neutral empty directory to run classic-mode vcpkg commands in.
+--
+-- vcpkg switches to manifest mode when a vcpkg.json exists in the current working directory, and
+-- in manifest mode it rejects individual package arguments ("In manifest mode, `vcpkg install`
+-- does not support individual package arguments"). so classic-mode commands (install/list/
+-- depend-info, which pass `<pkg>:<triplet>`) must not run from a project directory that ships its
+-- own vcpkg.json.
+-- @see https://github.com/xmake-io/xmake/issues/7660
+function classic_curdir()
+ local dir = path.join(os.tmpdir(), "vcpkg", "classic")
+ if not os.isdir(dir) then
+ os.mkdir(dir)
+ end
+ return dir
+end
+
function is_installed(vcpkg, name, triplet, opt)
local argv = {"list", name .. ":" .. triplet, "--x-full-desc"}
local manifest_mode = need_manifest(opt)
@@ -46,7 +62,7 @@ function is_installed(vcpkg, name, triplet, opt)
end
local listinfo = try { function ()
- return os.iorunv(vcpkg, argv, manifest_mode and {curdir = opt.installdir} or nil)
+ return os.iorunv(vcpkg, argv, {curdir = manifest_mode and opt.installdir or classic_curdir()})
end}
if listinfo then
local exact_prefix = name .. ":" .. triplet
diff --git a/xmake/modules/package/manager/xmake/search_package.lua b/xmake/modules/package/manager/xmake/search_package.lua
index 32991ae3e..a5ed3caf8 100644
--- a/xmake/modules/package/manager/xmake/search_package.lua
+++ b/xmake/modules/package/manager/xmake/search_package.lua
@@ -23,7 +23,7 @@ import("core.base.semver")
import("private.xrepo.quick_search.cache")
function _search_package(packages, name, opt)
- for _, packageinfo in ipairs(cache.find(name, {description = opt.description ~= false})) do
+ for _, packageinfo in ipairs(cache.find(name, {description = opt.description ~= false, kind = opt.kind})) do
local packagename = packageinfo.name
local packagedata = packageinfo.data
@@ -59,7 +59,7 @@ end
-- search package using the xmake package manager
--
-- @param name the package name with pattern
--- @param opt the options, e.g. {require_version = "1.x"}
+-- @param opt the options, e.g. {require_version = "1.x", kind = "addon"}
--
function main(name, opt)
opt = opt or {}
diff --git a/xmake/modules/package/tools/cmake.lua b/xmake/modules/package/tools/cmake.lua
index 360a0f700..311b1c0af 100644
--- a/xmake/modules/package/tools/cmake.lua
+++ b/xmake/modules/package/tools/cmake.lua
@@ -858,17 +858,32 @@ function _get_envs_for_default_flags(package, configs, opt)
return table.clone(_get_default_flags(package, configs, buildtype, opt)) or {}
end
+-- quote flags that contain whitespace, so they survive as a single argument when cmake expands
+-- CMAKE_<LANG>_FLAGS (a space-separated string) onto the compiler/linker command line.
+-- e.g. -resource-dir=C:\Program Files\LLVM\lib\clang\22 would otherwise be split at the space.
+-- @see https://github.com/xmake-io/xmake/issues/7663
+function _quote_flags_with_spaces(flags)
+ if flags then
+ for idx, flag in ipairs(flags) do
+ if type(flag) == "string" and flag:find("%s") and not flag:startswith("\"") then
+ flags[idx] = "\"" .. flag .. "\""
+ end
+ end
+ end
+ return flags
+end
+
function _get_envs_for_runtime_flags(package, opt)
local buildtype = _get_cmake_buildtype(package)
local envs = {}
local runtimes = package:runtimes()
if runtimes then
- envs[format("CMAKE_C_FLAGS_%s", buildtype)] = toolchain_utils.map_compflags_for_package(package, "c", "runtime", runtimes)
- envs[format("CMAKE_CXX_FLAGS_%s", buildtype)] = toolchain_utils.map_compflags_for_package(package, "cxx", "runtime", runtimes)
- envs[format("CMAKE_EXE_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "binary", {"cxx"}, "runtime", runtimes)
- envs[format("CMAKE_STATIC_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "static", {"cxx"}, "runtime", runtimes)
- envs[format("CMAKE_SHARED_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes)
- envs[format("CMAKE_MODULE_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes)
+ envs[format("CMAKE_C_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_compflags_for_package(package, "c", "runtime", runtimes))
+ envs[format("CMAKE_CXX_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_compflags_for_package(package, "cxx", "runtime", runtimes))
+ envs[format("CMAKE_EXE_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "binary", {"cxx"}, "runtime", runtimes))
+ envs[format("CMAKE_STATIC_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "static", {"cxx"}, "runtime", runtimes))
+ envs[format("CMAKE_SHARED_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes))
+ envs[format("CMAKE_MODULE_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes))
end
return envs
end
@@ -1285,8 +1300,11 @@ function _shrink_cmake_arguments(argv, oldir, opt)
shrink = true
return true
end]]
- -- shrink long arguments
- if #v > 128 then
+ -- shrink long arguments, or arguments that carry quoted flags (e.g. a path with spaces).
+ -- routing them through set() in CMakeLists.txt keeps the embedded quotes intact, instead
+ -- of relying on the fragile multi-layer quoting of passing -D...="..." on the cmake cli.
+ -- @see https://github.com/xmake-io/xmake/issues/7663
+ if #v > 128 or v:find("\"", 1, true) then
local flags = v:replace("\"", "\\\"")
table.insert(cmake_argv, ("set(%s \"%s\")"):format(k, flags))
shrink = true
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
new file mode 100644
index 000000000..4cc5b0fba
--- /dev/null
+++ b/xmake/modules/private/action/addon/impl/install_addons.lua
@@ -0,0 +1,142 @@
+--!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 install_addons.lua
+--
+
+-- imports
+import("core.package.addon")
+import("core.project.addons")
+import("private.action.addon.impl.xrepo", {alias = "xrepo_addon"})
+
+-- get the requires of the declared addons
+--
+-- @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(declared, locked)
+ local requires = {}
+ for _, requirestr in ipairs(declared) do
+ local name = addons.requirename(requirestr)
+ local lockinfo = locked and locked[name]
+ if addons.locked_valid(requirestr, lockinfo) 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, declared)
+ local lockinfo = {}
+ local locked = addons.locked(projectdir) or {}
+
+ -- @note we need to reload the registry, they have been installed by another process,
+ -- 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(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
+ if oldversion and oldversion ~= addoninfo.version then
+ cprint("${color.success}upgrade ${bright}%s${clear}: %s -> %s", name, oldversion, addoninfo.version)
+ end
+ -- we lock the repository too, so that the other users get it from the same source,
+ -- @see xmake/modules/private/action/require/impl/lock_packages.lua
+ lockinfo[name] = {version = addoninfo.version, repo = addoninfo.repo}
+ end
+ lockinfo.__meta__ = {version = addons.lockfile_version()}
+
+ -- @note we need to write it deterministically, the key order of a lua table is random,
+ -- otherwise the lock file would change even if nothing changed,
+ -- @see xmake/modules/private/action/require/impl/lock_packages.lua
+ local content = string.serialize(lockinfo, {orderkeys = true})
+ local tmpfile = os.tmpfile()
+ io.writefile(tmpfile, content, {encoding = "binary"})
+
+ -- and we only write it if the content is different, so we can keep the file time
+ os.cp(tmpfile, addons.lockfile(projectdir), {copy_if_different = true})
+ os.rm(tmpfile)
+end
+
+-- 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
+--
+-- 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 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
+
+ -- 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
+ local repositories = table.wrap(declarations.repositories)
+ if #repositories > 0 then
+ rcfile = os.tmpfile() .. ".lua"
+ local file = io.open(rcfile, "w")
+ 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(declared, locked), {includes = rcfile})
+ end,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ if rcfile then
+ os.tryrm(rcfile)
+ end
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+
+ -- and lock them, so that the other users get the same versions
+ _lock_addons(projectdir, declared)
+end
diff --git a/xmake/modules/private/action/addon/impl/xrepo.lua b/xmake/modules/private/action/addon/impl/xrepo.lua
new file mode 100644
index 000000000..46ada2d01
--- /dev/null
+++ b/xmake/modules/private/action/addon/impl/xrepo.lua
@@ -0,0 +1,57 @@
+--!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 xrepo.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.package.addon")
+
+-- run the given xrepo action for the addons
+--
+-- @note xrepo installs the packages in its own working project, so it works anywhere,
+-- and we need not implement the download/dependencies/confirm logic again
+--
+-- @param action the action name, e.g. "install", "search"
+-- @param names the addon names, urls or require strings, e.g. {"esp32-devel 1.0.x"}
+-- @param opt the options, e.g. {force = true, includes = "/tmp/xxx.lua"}
+--
+-- @note we always run it in a working directory which has no project, @see addon.workdir()
+--
+function main(action, names, opt)
+ opt = opt or {}
+ local argv = {"lua", "private.xrepo", action, "--addon"}
+
+ -- we need to pass the common options to the sub-process, e.g. -y, -v, -D
+ for _, name in ipairs({"yes", "verbose", "diagnosis"}) do
+ if option.get(name) then
+ table.insert(argv, "--" .. name)
+ end
+ end
+ if opt.force then
+ table.insert(argv, "--force")
+ end
+
+ -- the extra lua configuration files, e.g. the repositories which a project declares
+ if opt.includes then
+ table.insert(argv, "--includes=" .. opt.includes)
+ end
+
+ table.join2(argv, names)
+ os.execv(os.programfile(), argv, {curdir = opt.curdir or addon.workdir()})
+end
diff --git a/xmake/modules/private/action/build/pcheader.lua b/xmake/modules/private/action/build/pcheader.lua
index 4e984f62b..1682ead8c 100644
--- a/xmake/modules/private/action/build/pcheader.lua
+++ b/xmake/modules/private/action/build/pcheader.lua
@@ -38,12 +38,24 @@ function config(target, langkind, opt)
-- https://github.com/xmake-io/xmake/issues/2667
-- https://github.com/xmake-io/xmake/issues/5858
if not os.isfile(headerfile) then
- io.writefile(headerfile, ([[
+ local langcxx = (langkind == "cxx" or langkind == "mxx")
+ local content
+ if langcxx then
+ content = [[
#pragma system_header
#ifdef __cplusplus
#include "%s"
#endif // __cplusplus
- ]]):format(path.absolute(pcheaderfile):gsub("\\", "/")))
+ ]]
+ else
+ content = [[
+#pragma system_header
+#ifndef __cplusplus
+#include "%s"
+#endif
+ ]]
+ end
+ io.writefile(headerfile, content:format(path.absolute(pcheaderfile):gsub("\\", "/")))
end
-- we need only to add a header wrapper in .gch directory
-- @see https://github.com/xmake-io/xmake/issues/5858#issuecomment-2506918167
diff --git a/xmake/modules/private/action/require/impl/actions/download.lua b/xmake/modules/private/action/require/impl/actions/download.lua
index 55e43a3db..a8846f97b 100644
--- a/xmake/modules/private/action/require/impl/actions/download.lua
+++ b/xmake/modules/private/action/require/impl/actions/download.lua
@@ -216,6 +216,7 @@ function _download(package, url, sourcedir, opt)
else
http.download(url, packagefile, {
insecure = global.get("insecure-ssl"),
+ insecure_fallback = true, -- retry without ssl verification on cert error, the file is verified by sha256 below
headers = opt.url_http_headers or package:policy("package.download.http_headers")})
end
end
diff --git a/xmake/modules/private/action/require/impl/actions/download_resources.lua b/xmake/modules/private/action/require/impl/actions/download_resources.lua
index c185d25b0..dffa742b2 100644
--- a/xmake/modules/private/action/require/impl/actions/download_resources.lua
+++ b/xmake/modules/private/action/require/impl/actions/download_resources.lua
@@ -118,6 +118,7 @@ function _download(package, resource_name, resource_url, resource_hash)
elseif resource_url:find(string.ipattern("https-://")) or resource_url:find(string.ipattern("ftps-://")) then
http.download(resource_url, resource_file, {
insecure = global.get("insecure-ssl"),
+ insecure_fallback = true, -- retry without ssl verification on cert error, the file is verified by sha256 below
headers = package:policy("package.download.http_headers")})
else
raise("invalid resource url(%s)", resource_url)
diff --git a/xmake/modules/private/action/require/impl/actions/install.lua b/xmake/modules/private/action/require/impl/actions/install.lua
index 7fb54a951..c07add337 100644
--- a/xmake/modules/private/action/require/impl/actions/install.lua
+++ b/xmake/modules/private/action/require/impl/actions/install.lua
@@ -22,6 +22,7 @@
import("core.base.option")
import("core.base.tty")
import("core.package.package", {alias = "core_package"})
+import("core.package.addon")
import("core.project.target")
import("core.project.project")
import("core.platform.platform")
@@ -283,6 +284,50 @@ function _merge_staticlibs(package)
end
end
+-- register the installed addon, so that xmake can find its payloads, e.g. plugins
+--
+-- @note the addon manifest(addon.lua) is only read when installing it, everything which
+-- is needed later is recorded in the addons registry, @see core/package/addon.lua
+--
+function _register_addon(package)
+
+ -- get the addon deps from the package recipe
+ local deps
+ for _, dep in ipairs(package:plaindeps() or {}) do
+ if dep:is_addon() then
+ deps = deps or {}
+ table.insert(deps, dep:name())
+ end
+ end
+
+ -- the addon describes itself? we prefer its own description
+ --
+ -- @note the deps are duplicated in its manifest, but the recipe is authoritative,
+ -- xmake needs them before downloading the addon sources, so we only report the
+ -- mismatch, they must be kept in sync
+ --
+ local description = package:description()
+ local manifest = package:data("addon.manifest")
+ if manifest then
+ description = manifest.description or description
+ for _, dep in ipairs(manifest.deps) do
+ if not (deps and table.contains(deps, dep)) then
+ wprint("addon(%s): dep(%s) is declared in its manifest, but not in the package recipe!",
+ package:name(), dep)
+ end
+ end
+ end
+
+ -- we also record where it comes from, so that the projects can lock it,
+ -- @see xmake/modules/private/action/addon/impl/install_addons.lua
+ local repo = package:repo()
+ addon.register(package:name(), package:version_str() or "latest",
+ {description = description, deps = deps,
+ repo = repo and {url = repo:url(), commit = repo:commit(), branch = repo:branch()} or nil,
+ manifest_deps = manifest and manifest.deps or nil,
+ globalmodules = manifest and #manifest.globalmodules > 0 and manifest.globalmodules or nil})
+end
+
-- get failed install directory
function _get_installdir_failed(package)
return path.join(package:cachedir(), "installdir.failed")
@@ -499,6 +544,11 @@ function main(package)
-- save the package info to the manifest file
package:manifest_save()
+
+ -- register this addon, so that xmake can find its payloads, e.g. plugins
+ if package:is_addon() then
+ _register_addon(package)
+ end
installed_now = true
end
end
@@ -568,6 +618,13 @@ function main(package)
end
os.tryrm(installdir)
+ -- the addon is registered before testing it, its `on_test` needs to use it,
+ -- e.g. xmake create -t esp32.blink, so we need to unregister it again here,
+ -- otherwise it would be seen as installed while its files are gone
+ if package:is_addon() then
+ addon.unregister(package:name(), package:version_str() or "latest")
+ end
+
-- is not last scheme? we can fallback to next scheme and try reinstall it again
local current_scheme = package:current_scheme()
local schemes_orderlist = package:schemes_orderlist()
diff --git a/xmake/modules/private/action/require/impl/actions/patch_sources.lua b/xmake/modules/private/action/require/impl/actions/patch_sources.lua
index 6c18b69b1..6b8e60e59 100644
--- a/xmake/modules/private/action/require/impl/actions/patch_sources.lua
+++ b/xmake/modules/private/action/require/impl/actions/patch_sources.lua
@@ -80,6 +80,7 @@ function _patch(package, patchinfo)
if patch_url:find(string.ipattern("https-://")) or patch_url:find(string.ipattern("ftps-://")) then
http.download(patch_url, patch_file, {
insecure = global.get("insecure-ssl"),
+ insecure_fallback = true, -- retry without ssl verification on cert error, the file is verified by sha256 below
headers = package:policy("package.download.http_headers")})
else
-- copy the patch file
diff --git a/xmake/modules/private/action/require/impl/install_packages.lua b/xmake/modules/private/action/require/impl/install_packages.lua
index f0da9eb2e..2d633109e 100644
--- a/xmake/modules/private/action/require/impl/install_packages.lua
+++ b/xmake/modules/private/action/require/impl/install_packages.lua
@@ -167,6 +167,13 @@ function _get_confirm_from_3rd(packages)
end
-- get user confirm
+--
+-- @param packages the packages to be installed
+-- @param opt the options
+-- - toolchain: these packages are toolchain packages, we will show a different tip for it
+--
+-- @return the confirm result and the modified packages
+--
function _get_confirm(packages, opt)
opt = opt or {}
@@ -204,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
@@ -683,6 +692,11 @@ function _get_package_installdeps(packages)
end
-- install packages
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options, @see main
+-- - toolchain: only install the toolchain packages and their dependent packages
+--
function _install_packages(requires, opt)
opt = opt or {}
@@ -845,8 +859,15 @@ end
-- install all required packages
--
--- @param requires the requires table
--- @param opt the options
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options, it will be passed to `package.load_packages` directly
+-- - requires_extra: the extra require configs from `add_requires()`, indexed by the require string
+-- - nodeps: only install the given packages, do not install their dependent packages
+-- - system: load package from system if `true`, and never load it if `false` (only for non-3rd packages)
+-- - packagekind: the package kind, e.g. "addon", it will be loaded from the `addons` root directory of repositories
+-- @note `toolchain` is reserved and it will be set internally, @see load_packages
+--
+-- @return the installed packages, including the toolchain packages and all dependent packages
--
function main(requires, opt)
-- we need to install toolchain packages first,
diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua
index 3ad758c3a..1105384c8 100644
--- a/xmake/modules/private/action/require/impl/package.lua
+++ b/xmake/modules/private/action/require/impl/package.lua
@@ -43,6 +43,15 @@ function _memcache()
end
-- load require info
+--
+-- @param require_str the require string, e.g. "zlib >=1.2.11", "libplist[shared,debug]"
+-- @param requires_extra the extra require configs from `add_requires()`, indexed by the require string
+-- @param opt the options
+-- - requirepath: the parent require path, e.g. "foo.bar", it's used to get the resolved requireinfo
+-- - resolvedinfo: the resolved requireinfo of dependency conflicts, indexed by require path
+--
+-- @return the package name and requireinfo
+--
function _load_require(require_str, requires_extra, opt)
opt = opt or {}
@@ -183,6 +192,15 @@ function _load_package_from_project(packagename)
end
-- load package package from repositories
+--
+-- @param packagename the package name
+-- @param opt the options
+-- - plat: the given platform of this package
+-- - arch: the given architecture of this package
+-- - name: the given repository name, we will only find this package in the given repository
+-- - rootdir: the root directory of repositories, e.g. "packages" (default), "addons"
+-- - locked_repo: the locked repository info in `xmake-requires.lock`, e.g. {url = .., commit = .., branch = ..}
+--
function _load_package_from_repository(packagename, opt)
opt = opt or {}
local packagedir, repo = repository.packagedir(packagename, opt)
@@ -191,7 +209,31 @@ function _load_package_from_repository(packagename, opt)
end
end
+-- get the root directory of repositories for the given package
+--
+-- e.g. "packages" (default), "addons", "plugins" (deprecated)
+--
+-- @note the addon packages are only searched from the `addons` root directory,
+-- and we need to set it explicitly, e.g. add_deps("foo", {kind = "addon"})
+--
+function _get_repository_rootdir(requireinfo, opt)
+ local packagekind = requireinfo.kind or opt.packagekind
+ if packagekind == "addon" then
+ return "addons"
+ elseif packagekind == "plugin" then
+ return "plugins"
+ end
+ return "packages"
+end
+
-- load package package from base
+--
+-- e.g. package("foo") set_base("bar")
+--
+-- @param package the package instance
+-- @param basename the base package name
+-- @param opt the options, @see _load_package_from_repository
+--
function _load_package_from_base(package, basename, opt)
local package_base = _load_package_from_project(basename)
if not package_base then
@@ -203,6 +245,10 @@ function _load_package_from_base(package, basename, opt)
end
-- has locked requires?
+--
+-- @param opt the options
+-- - force: force to use the locked requires even if `xmake require --upgrade` is called
+--
function _has_locked_requires(opt)
opt = opt or {}
if not option.get("upgrade") or opt.force then
@@ -211,6 +257,13 @@ function _has_locked_requires(opt)
end
-- get locked requires
+--
+-- @param requirekey the require key in `xmake-requires.lock`, @see _get_packagelock_key
+-- @param opt the options
+-- - force: force to reload `xmake-requires.lock` and ignore `--upgrade`
+--
+-- @return the locked requireinfo and the version of `xmake-requires.lock`
+--
function _get_locked_requires(requirekey, opt)
opt = opt or {}
local requireslock = _memcache():get("requireslock")
@@ -260,6 +313,10 @@ end
--
-- orderdeps: a -> b -> c
--
+-- @param package the package instance
+-- @param opt the options
+-- - private: also sort the private library deps, e.g. add_deps("foo", {private = true})
+--
function _sort_librarydeps(package, opt)
-- we must use native deps list instead of package:deps() to generate correct link order
local orderdeps = {}
@@ -564,6 +621,13 @@ function _match_requirepath(requirepath, requireconf)
end
-- init requireinfo
+--
+-- @param requireinfo the requireinfo
+-- @param package the package instance
+-- @param opt the options
+-- - is_toplevel: this package is a toplevel package in `add_requires()`, but not a dependent package,
+-- and we will pass some root configs to it, e.g. toolchains, runtimes, lto, asan ..
+--
function _init_requireinfo(requireinfo, package, opt)
-- pass root configs to top library package
requireinfo.configs = requireinfo.configs or {}
@@ -899,11 +963,35 @@ function _select_package_runtimes(package)
end
end
--- load required packages
+-- load the given required package
+--
+-- we will load it from the project, repositories and system in order,
+-- and the requireinfo will be initialized and attached to the package instance.
+--
+-- @param packagename the package name, e.g. "zlib", "zlib~debug", "vcpkg::zlib"
+-- @param requireinfo the requireinfo, @see _load_require
+-- @param opt the options
+-- - system: load package from system if `true`, and never load it if `false`,
+-- it's only used when `add_requires("zlib", {system = nil})` is not set (only for non-3rd packages)
+-- - packagekind: the package kind, e.g. "addon", it will be loaded from the `addons` root directory of repositories
+-- - toolchain: only load toolchain packages, the non-toolchain toplevel packages will be ignored
+-- - requirepath: the current require path, e.g. "foo.bar", it's used to detect circular dependencies
+-- and match `add_requireconfs()`
+-- - parentinfo: the parent requireinfo, this package will inherit some builtin configs from it, e.g. runtimes, pic
+--
+-- @return the package instance, it will be nil if this package is filtered by `opt.toolchain`,
+-- and it will raise an error if this package is not found in any repositories
+--
function _load_package(packagename, requireinfo, opt)
-- check circular dependency
opt = opt or {}
+
+ -- the `addon` and `self` names are reserved, we use them to reference the addon resources,
+ -- e.g. add_rules("@addon/esp32/flash"), import("@self.sdkconfig")
+ if packagename == "addon" or packagename == "self" then
+ raise("package(%s): the name `%s` is reserved by xmake for the addon references, please rename it!", packagename, packagename)
+ end
if opt.requirepath then
local splitinfo = opt.requirepath:split(".", {plain = true})
if #splitinfo > 3 and
@@ -949,6 +1037,7 @@ function _load_package(packagename, requireinfo, opt)
plat = requireinfo.plat,
arch = requireinfo.arch,
name = requireinfo.reponame,
+ rootdir = _get_repository_rootdir(requireinfo, opt),
locked_repo = locked_requireinfo and locked_requireinfo.repo})
if package then
from_repo = true
@@ -958,7 +1047,9 @@ function _load_package(packagename, requireinfo, opt)
-- load base package
if package and package:get("base") then
_load_package_from_base(package, package:get("base"), {
- name = requireinfo.reponame, locked_repo = locked_requireinfo and locked_requireinfo.repo})
+ name = requireinfo.reponame,
+ rootdir = _get_repository_rootdir(requireinfo, opt),
+ locked_repo = locked_requireinfo and locked_requireinfo.repo})
end
-- load package from system
@@ -1105,7 +1196,14 @@ function _load_package(packagename, requireinfo, opt)
return package
end
--- load all required packages
+-- load all required packages and their dependent packages
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options, @see load_packages
+--
+-- @return the packages with all dependent packages (the deps are always in front of their parents),
+-- and the packages without deps
+--
function _load_packages(requires, opt)
-- no requires?
@@ -1420,6 +1518,11 @@ end
-- compatible with all previous link dependencies?
-- @see https://github.com/xmake-io/xmake/issues/2719
+--
+-- @param package the package instance
+-- @param opt the options
+-- - install_finished: the installation has been finished, we do not need to check compatibility again
+--
function _compatible_with_previous_librarydeps(package, opt)
-- skip to check compatibility if installation has been finished
@@ -1524,6 +1627,13 @@ function cachedir()
end
-- this package should be install?
+--
+-- @param package the package instance
+-- @param opt the options
+-- - install_finished: the installation has been finished, it's used to check if this package
+-- has been installed successfully, and we will ignore `package.install_always`
+-- policy and the librarydeps compatibility checking
+--
function should_install(package, opt)
opt = opt or {}
if package:is_template() then
@@ -1592,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 {}
@@ -1631,7 +1744,15 @@ function get_configs_str(package)
return configs_str
end
--- get locked requireinfo
+-- get locked requireinfo from `xmake-requires.lock`
+--
+-- @param requireinfo the requireinfo, it must contain the requirekey
+-- @param opt the options
+-- - force: force to reload `xmake-requires.lock` and ignore `--upgrade`
+--
+-- @return the locked requireinfo and the version of `xmake-requires.lock`,
+-- it will be nil if the lock file does not exist or its version is incompatible
+--
function get_locked_requireinfo(requireinfo, opt)
local requirekey = requireinfo.requirekey
local locked_requireinfo, requireslock_version
@@ -1645,6 +1766,13 @@ function get_locked_requireinfo(requireinfo, opt)
end
-- load requires
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param requires_extra the extra require configs from `add_requires()`, indexed by the require string
+-- @param opt the options, @see _load_require
+--
+-- @return the require items, e.g. {{name = "zlib", info = {version = ">=1.2.11", ..}}, ..}
+--
function load_requires(requires, requires_extra, opt)
opt = opt or {}
local requireitems = {}
@@ -1656,6 +1784,18 @@ function load_requires(requires, requires_extra, opt)
end
-- load all required packages
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options
+-- - requires_extra: the extra require configs from `add_requires()`, e.g. {["zlib >=1.2.11"] = {configs = {shared = true}}}
+-- - nodeps: only load the given packages, do not load their dependent packages
+-- - system: load package from system if `true`, and never load it if `false` (only for non-3rd packages)
+-- - packagekind: the package kind, e.g. "addon", it will be loaded from the `addons` root directory of repositories
+-- - toolchain: only load toolchain packages and their dependent packages
+-- - requirepath: the parent require path, e.g. "foo.bar", it's used to detect circular dependencies and match `add_requireconfs()`
+-- - parentinfo: the parent requireinfo, the child package will inherit some builtin configs from it, e.g. runtimes, pic
+-- - resolvedinfo: the resolved requireinfo of dependency conflicts, it's only used to reload packages internally
+--
function load_packages(requires, opt)
opt = opt or {}
local unique = {}
diff --git a/xmake/modules/private/action/require/impl/repository.lua b/xmake/modules/private/action/require/impl/repository.lua
index 3df59c42c..d17bdb570 100644
--- a/xmake/modules/private/action/require/impl/repository.lua
+++ b/xmake/modules/private/action/require/impl/repository.lua
@@ -151,6 +151,9 @@ function pulled()
end
-- get package directory from repositories
+--
+-- @param packagename the package name
+-- @param opt {rootdir = "packages|addons|plugins"}
function packagedir(packagename, opt)
-- strip trailing ~tag, e.g. zlib~debug
@@ -162,7 +165,8 @@ function packagedir(packagename, opt)
-- get cache key
local reponame = opt.name
- local cachekey = packagename
+ local rootdir = opt.rootdir or "packages"
+ local cachekey = rootdir .. "/" .. packagename
local locked_repo = opt.locked_repo
if locked_repo then
cachekey = cachekey .. locked_repo.url .. (locked_repo.commit or "") .. (locked_repo.branch or "")
@@ -185,7 +189,7 @@ function packagedir(packagename, opt)
-- find the package directory from repositories
if not foundir then
for _, repo in ipairs(repositories()) do
- local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1), packagename)
+ local dir = path.join(repo:directory(), rootdir, packagename:sub(1, 1), packagename)
if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) and (not reponame or reponame == repo:name()) then
foundir = {dir, repo}
break
diff --git a/xmake/modules/private/action/require/install.lua b/xmake/modules/private/action/require/install.lua
index b7ba9ef5e..ebc648124 100644
--- a/xmake/modules/private/action/require/install.lua
+++ b/xmake/modules/private/action/require/install.lua
@@ -82,7 +82,14 @@ function main(requires_raw)
-- install packages
environment.enter()
- local packages = install_packages(requires, {requires_extra = requires_extra})
+ -- @note the `--plugin` option is deprecated, please use `--addon` instead
+ local packagekind = "package"
+ if option.get("addon") then
+ packagekind = "addon"
+ elseif option.get("plugin") then
+ packagekind = "plugin"
+ end
+ local packages = install_packages(requires, {packagekind = packagekind, requires_extra = requires_extra})
if packages then
_check_missing_packages(packages)
end
diff --git a/xmake/modules/private/action/require/search.lua b/xmake/modules/private/action/require/search.lua
index e714c6c19..a0f98e0d9 100644
--- a/xmake/modules/private/action/require/search.lua
+++ b/xmake/modules/private/action/require/search.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.task")
+import("core.base.option")
import("private.action.require.impl.utils.filter")
import("private.action.require.impl.repository")
import("private.action.require.impl.environment")
@@ -41,11 +42,14 @@ function main(names)
task.run("repo", {update = true})
end
+ -- we only search the addon packages if `--addon` is enabled
+ local kind = option.get("addon") and "addon" or nil
+
-- show title
- print("The package names:")
+ print(kind == "addon" and "The addon names:" or "The package names:")
-- search packages
- for name, packages in pairs(search_packages(names)) do
+ for name, packages in pairs(search_packages(names, {kind = kind})) do
if #packages > 0 then
-- show name
diff --git a/xmake/modules/private/check/checkers/api/package/kind.lua b/xmake/modules/private/check/checkers/api/package/kind.lua
index 4a9c309e3..2e4f2495b 100644
--- a/xmake/modules/private/check/checkers/api/package/kind.lua
+++ b/xmake/modules/private/check/checkers/api/package/kind.lua
@@ -37,6 +37,8 @@ function main(opt)
end
return true
end
- return value == "binary" or value == "toolchain" or value == "template"
+ -- @note the `plugin` kind is deprecated, please use the `addon` kind instead
+ return value == "binary" or value == "toolchain" or value == "template" or
+ value == "addon" or value == "plugin"
end}))
end
diff --git a/xmake/modules/private/utils/batchcmds.lua b/xmake/modules/private/utils/batchcmds.lua
index 3f584b286..349e31efe 100644
--- a/xmake/modules/private/utils/batchcmds.lua
+++ b/xmake/modules/private/utils/batchcmds.lua
@@ -28,6 +28,7 @@ import("core.theme.theme")
import("core.tool.linker")
import("core.tool.compiler")
import("core.language.language")
+import("core.sandbox.sandbox")
import("utils.run_script")
import("utils.progress", {alias = "progress_utils"})
import("utils.binary.rpath", {alias = "rpath_utils"})
@@ -126,6 +127,20 @@ function _runcmd_vlua(cmd, opt)
end
end
+-- run command: call
+function _runcmd_call(cmd, opt)
+ local func = cmd.func
+ if func then
+ if not opt.dryrun then
+ local argv = table.clone(cmd.argv)
+ if cmd.opt then
+ table.insert(argv, cmd.opt)
+ end
+ func(table.unpack(argv))
+ end
+ end
+end
+
-- run command: os.mkdir
function _runcmd_mkdir(cmd, opt)
local dir = cmd.dir
@@ -222,6 +237,7 @@ function _runcmd(cmd, opt)
vexecv = _runcmd_vexecv,
lua = _runcmd_lua,
vlua = _runcmd_vlua,
+ call = _runcmd_call,
mkdir = _runcmd_mkdir,
rmdir = _runcmd_rmdir,
cd = _runcmd_cd,
@@ -305,11 +321,23 @@ function batchcmds:lua(script, argv, opt)
table.insert(self:cmds(), {kind = "lua", script = script, argv = argv, opt = opt})
end
--- add command: run lua script file, command or module
+-- add command: run lua script, command or module
function batchcmds:vlua(script, argv, opt)
table.insert(self:cmds(), {kind = "vlua", script = script, argv = argv, opt = opt})
end
+-- add command: call lua function
+function batchcmds:call(func, argv, opt)
+ local functype = type(func)
+ if functype == "string" then
+ self:lua(func, argv, opt)
+ else
+ assert(functype == "function")
+ sandbox.fork(func)
+ table.insert(self:cmds(), {kind = "call", func = func, argv = argv, opt = opt})
+ end
+end
+
-- add command: compile source files
--
-- @param sourcefiles the source file paths
diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua
index c2840a591..3b4fe8296 100644
--- a/xmake/modules/private/utils/toolchain.lua
+++ b/xmake/modules/private/utils/toolchain.lua
@@ -343,6 +343,25 @@ function map_linkflags_for_package(package, targetkind, sourcekinds, name, value
return flags
end
+-- ask the clang driver for the absolute path of the given library
+--
+-- clang knows its own library search paths, e.g. the per-target runtime directory
+-- (`lib/<target-triple>/libc++.a`), and it also works when the toolchain has no `--sdk=/xxx/llvm`.
+--
+-- it just echoes back the given filename if the library does not exist,
+-- so we only accept an existing absolute path.
+function _get_llvm_libfile(toolchain, clang, libname, opt)
+ local argv = table.wrap(get_clang_target_flags(toolchain))
+ table.insert(argv, "-print-file-name=" .. libname)
+ local libfile = try {function () return os.iorunv(clang, argv, {envs = opt.envs}) end}
+ if libfile then
+ libfile = path.normalize(libfile:trim())
+ if path.is_absolute(libfile) and os.isfile(libfile) then
+ return libfile
+ end
+ end
+end
+
-- Check and cache llvm/clang driver info (e.g. -print-resource-dir/-print-target-triple) in toolchain:config().
-- It must be collected during toolchain on_check so that toolchain on_load can use it without running clang again.
-- @see https://github.com/xmake-io/xmake/issues/7337#issuecomment-3942499208
@@ -370,6 +389,17 @@ function check_llvm_info(toolchain, clang, opt)
toolchain:config_set("llvm_target_triple", target_triple)
end
end
+
+ -- the static c++ runtime archives, they may be in the per-target runtime directory,
+ -- e.g. `lib/x86_64-unknown-linux-gnu/libc++.a`
+ -- @see https://github.com/xmake-io/xmake/issues/7442
+ for libname, configname in pairs({["libc++.a"] = "llvm_libcxx_static", ["libc++abi.a"] = "llvm_libcxxabi_static"}) do
+ local libfile = _get_llvm_libfile(toolchain, clang, libname, opt)
+ if libfile then
+ dprint("checking for llvm %s ... %s", libname, libfile)
+ toolchain:config_set(configname, libfile)
+ end
+ end
end
-- get llvm sdk resource directory
@@ -457,6 +487,11 @@ function get_llvm_dirs(toolchain)
end
local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resourcedir, rtdir, rtlink, rtlibdir
+
+ -- the clang driver has already resolved the static c++ runtime archives on check
+ -- @see check_llvm_info
+ local libcxx_static = toolchain:config("llvm_libcxx_static")
+ local libcxxabi_static = toolchain:config("llvm_libcxxabi_static")
if rootdir then
bindir = path.join(rootdir, "bin")
bindir = os.isdir(bindir) and bindir or nil
@@ -476,6 +511,19 @@ function get_llvm_dirs(toolchain)
end
end
+ -- fallback to search the static c++ runtime archives in the library directories
+ if libdir and not (libcxx_static and libcxxabi_static) then
+ local staticdirs = cxxlibdir and {cxxlibdir, libdir} or {libdir}
+ for _, dir in ipairs(staticdirs) do
+ if not libcxx_static and os.isfile(path.join(dir, "libc++.a")) then
+ libcxx_static = path.join(dir, "libc++.a")
+ end
+ if not libcxxabi_static and os.isfile(path.join(dir, "libc++abi.a")) then
+ libcxxabi_static = path.join(dir, "libc++abi.a")
+ end
+ end
+ end
+
includedir = path.join(rootdir, "include")
includedir = os.isdir(includedir) and includedir or nil
@@ -502,7 +550,9 @@ function get_llvm_dirs(toolchain)
resourcedir = resourcedir,
rtdir = rtdir,
rtlibdir = rtlibdir,
- rtlink = rtlink }
+ rtlink = rtlink,
+ libcxx_static = libcxx_static,
+ libcxxabi_static = libcxxabi_static }
memcache:set(cachekey, llvm_dirs)
end
return llvm_dirs
@@ -645,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/modules/private/xrepo/action/install.lua b/xmake/modules/private/xrepo/action/install.lua
index b0d297b38..4abb57c7f 100644
--- a/xmake/modules/private/xrepo/action/install.lua
+++ b/xmake/modules/private/xrepo/action/install.lua
@@ -47,6 +47,9 @@ function menu_options()
"e.g.",
" - xrepo install -p cross --toolchain=mytool --includes='toolchain1.lua" .. path.envsep() .. "toolchain2.lua'"},
{nil, "policies", "kv", nil, "Set the policies." },
+ {nil, "addon", "k", nil, "Install addon packages from <repository>/addons/"},
+ {nil, "plugin", "k", nil, "Install plugin packages from <repository>/plugins/",
+ "(deprecated, please use --addon instead)"},
{category = "Visual Studio SDK Configuration" },
{nil, "vs", "kv", nil, "The Microsoft Visual Studio"
, " e.g. --vs=2017" },
@@ -281,6 +284,12 @@ function _install_packages(packages)
if option.get("build") or is_debug then
table.insert(require_argv, "--build")
end
+ if option.get("addon") then
+ table.insert(require_argv, "--addon")
+ end
+ if option.get("plugin") then
+ table.insert(require_argv, "--plugin")
+ end
local extra = {system = false}
if mode == "debug" then
extra.debug = true
diff --git a/xmake/modules/private/xrepo/action/remove.lua b/xmake/modules/private/xrepo/action/remove.lua
index ed92a83b8..eed956c6e 100644
--- a/xmake/modules/private/xrepo/action/remove.lua
+++ b/xmake/modules/private/xrepo/action/remove.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.option")
+import("core.package.addon")
import("private.action.require.impl.remove_packages", {alias = "remove_all_packages"})
-- get menu options
@@ -44,6 +45,10 @@ function menu_options()
{nil, "toolchain", "kv", nil, "Set the toolchain name." },
{nil, "toolchain_host", "kv", nil, "Set the host toolchain name." },
{ },
+ {nil, "addon", "k", nil, "Remove the given installed addon packages.",
+ "e.g.",
+ " - xrepo remove --addon serial-monitor" },
+ {'f', "force", "k", nil, "Force to remove the addon packages, even if they are depended on by the others." },
{nil, "all", "k", nil, "Remove all packages and ignore extra package configs.",
"If `--all` is enabled, the package name parameter will support lua pattern",
"e.g.",
@@ -193,10 +198,21 @@ function _remove_packages(packages)
os.vexecv(os.programfile(), require_argv)
end
+-- remove the given installed addons
+function _remove_addons(names)
+ for _, name in ipairs(names) do
+ addon.remove(name, {force = option.get("force")})
+ cprint("${color.success}remove ${bright}%s${clear} ok!", name)
+ end
+end
+
-- main entry
function main()
local packages = option.get("packages")
- if option.get("all") then
+ if option.get("addon") then
+ assert(packages, "please specify the addons to be removed.")
+ _remove_addons(packages)
+ elseif option.get("all") then
remove_all_packages(packages)
elseif packages then
_remove_packages(packages)
diff --git a/xmake/modules/private/xrepo/action/search.lua b/xmake/modules/private/xrepo/action/search.lua
index d389e3833..c532af44e 100644
--- a/xmake/modules/private/xrepo/action/search.lua
+++ b/xmake/modules/private/xrepo/action/search.lua
@@ -30,6 +30,9 @@ function menu_options()
-- menu options
local options =
{
+ {nil, "addon", "k", nil, "Search the addon packages from <repository>/addons/",
+ "e.g.",
+ " - xrepo search --addon serial"},
{nil, "packages", "vs", nil, "The packages list (support lua pattern).",
"e.g.",
" - xrepo search zlib boost",
@@ -80,6 +83,9 @@ function _search_packages(packages)
if option.get("diagnosis") then
table.insert(require_argv, "-D")
end
+ if option.get("addon") then
+ table.insert(require_argv, "--addon")
+ end
table.join2(require_argv, packages)
os.vexecv(os.programfile(), require_argv)
end
diff --git a/xmake/modules/private/xrepo/quick_search/cache.lua b/xmake/modules/private/xrepo/quick_search/cache.lua
index cf75a212d..feba8cda9 100644
--- a/xmake/modules/private/xrepo/quick_search/cache.lua
+++ b/xmake/modules/private/xrepo/quick_search/cache.lua
@@ -25,20 +25,36 @@ import("private.action.require.impl.repository")
local cache = globalcache.cache("quick_search")
+-- get the cache key of the given package
+--
+-- @note the addons are stored with the `addon::` prefix,
+-- because an addon and a package may have the same name
+--
+function _cachekey(packagename, kind)
+ return kind == "addon" and ("addon::" .. packagename) or packagename
+end
+
-- search package directories from repositories
+--
+-- the packages are stored in <repodir>/packages/<first-letter>/<name>,
+-- and the addons are stored in <repodir>/addons/<first-letter>/<name>
+--
function _list_package_dirs()
-- find the package directories from all repositories
local unique = {}
local packageinfos = {}
for _, repo in ipairs(repository.repositories()) do
- for _, file in ipairs(os.files(path.join(repo:directory(), "packages", "*", "*", "xmake.lua"))) do
- local dir = path.directory(file)
- local subdirname = path.basename(path.directory(dir))
- if #subdirname == 1 then -- ignore l/luajit/port/xmake.lua
- local packagename = path.filename(dir)
- if not unique[packagename] then
- table.insert(packageinfos, {name = packagename, repo = repo, packagedir = dir})
- unique[packagename] = true
+ for _, rootinfo in ipairs({{rootdir = "packages"}, {rootdir = "addons", kind = "addon"}}) do
+ for _, file in ipairs(os.files(path.join(repo:directory(), rootinfo.rootdir, "*", "*", "xmake.lua"))) do
+ local dir = path.directory(file)
+ local subdirname = path.basename(path.directory(dir))
+ if #subdirname == 1 then -- ignore l/luajit/port/xmake.lua
+ local packagename = path.filename(dir)
+ local cachekey = _cachekey(packagename, rootinfo.kind)
+ if not unique[cachekey] then
+ table.insert(packageinfos, {name = packagename, kind = rootinfo.kind, repo = repo, packagedir = dir})
+ unique[cachekey] = true
+ end
end
end
end
@@ -57,7 +73,9 @@ end
function update()
for _, packageinfo in ipairs(_list_package_dirs()) do
local package = core_package.load_from_repository(packageinfo.name, packageinfo.packagedir, {repo = packageinfo.repo})
- cache:set(packageinfo.name, {
+ cache:set(_cachekey(packageinfo.name, packageinfo.kind), {
+ name = packageinfo.name,
+ kind = packageinfo.kind,
reponame = package:repo() and package:repo():name(),
description = package:description(),
versions = package:versions(),
@@ -79,22 +97,30 @@ function get()
end
-- find package
+--
+-- @param name the package name (support lua pattern)
+-- @param opt the options, e.g. {prefix = true, description = true, kind = "addon"}
+--
function find(name, opt)
_init()
opt = opt or {}
local list_result = {}
- for packagename, packagedata in pairs(cache:data()) do
- local found = false
- if opt.prefix then
- found = packagename:startswith(name)
- else
- found = packagename:find(path.pattern(name))
- end
- if not found and opt.description and packagedata.description and packagedata.description:find(name) then
- found = true
- end
- if found then
- table.insert(list_result, {name = packagename, data = packagedata})
+ for cachekey, packagedata in table.orderpairs(cache:data()) do
+ -- we only search the packages with the given kind, e.g. nil (package), "addon"
+ local packagename = packagedata.name or cachekey
+ if packagedata.kind == opt.kind then
+ local found = false
+ if opt.prefix then
+ found = packagename:startswith(name)
+ else
+ found = packagename:find(path.pattern(name))
+ end
+ if not found and opt.description and packagedata.description and packagedata.description:find(name) then
+ found = true
+ end
+ if found then
+ table.insert(list_result, {name = packagename, data = packagedata})
+ end
end
end
return list_result