summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-08-06 20:56:46 +0800
committerGitHub <[email protected]>2026-08-06 20:56:46 +0800
commit71bc0ee880fa6be6452cbea2dddf00f45ff43c7e (patch)
tree95f374bcd4a3827dbc3b4b5be7c85250e8bdcc8e
parent53c06b72cc8b1521aae266543aee634c917a030f (diff)
parente129ec374599dfddb45f1a157a64f7330f9a6124 (diff)
Merge pull request #7688 from xmake-io/runtime
improve clang static libc++ runtimes
-rw-r--r--xmake/languages/c++/xmake.lua12
-rw-r--r--xmake/modules/core/tools/clang.lua46
-rw-r--r--xmake/modules/private/utils/toolchain.lua52
3 files changed, 102 insertions, 8 deletions
diff --git a/xmake/languages/c++/xmake.lua b/xmake/languages/c++/xmake.lua
index b94a073b8..42ddc2884 100644
--- a/xmake/languages/c++/xmake.lua
+++ b/xmake/languages/c++/xmake.lua
@@ -60,8 +60,7 @@ language("c++")
, "toolchain.sysincludedirs"
}
, binary = {
- "target.runtimes"
- , "config.linkdirs"
+ "config.linkdirs"
, "config.frameworkdirs"
, "target.linkdirs"
, "target.frameworkdirs"
@@ -79,13 +78,16 @@ language("c++")
, "config.frameworks"
, "target.frameworks"
, "toolchain.frameworks"
+ -- runtimes may link libc++.a/libc++abi.a explicitly, so it must come after the
+ -- object files and user links, but before the syslinks it depends on (e.g. pthread)
+ -- @see https://github.com/xmake-io/xmake/issues/7442
+ , "target.runtimes"
, "config.syslinks"
, "target.syslinks"
, "toolchain.syslinks"
}
, shared = {
- "target.runtimes"
- , "config.linkdirs"
+ "config.linkdirs"
, "config.frameworkdirs"
, "target.linkdirs"
, "target.frameworkdirs"
@@ -103,6 +105,8 @@ language("c++")
, "config.frameworks"
, "target.frameworks"
, "toolchain.frameworks"
+ -- @see https://github.com/xmake-io/xmake/issues/7442
+ , "target.runtimes"
, "config.syslinks"
, "target.syslinks"
, "toolchain.syslinks"
diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua
index d9d6512ee..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,9 +297,36 @@ function nf_runtime(self, runtime, opt)
end
end
end
- if runtime:endswith("_static") and _has_static_libstdcxx(self) then
- maps["c++_static"] = table.join(maps["c++_static"], "-static-libstdc++")
- 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/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua
index c2840a591..bd673fa0c 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