diff options
| author | ruki <[email protected]> | 2025-12-18 15:07:54 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-18 15:07:54 +0800 |
| commit | 67019f3310fe67e365c24df2a47806bf3fd947e5 (patch) | |
| tree | 54c91c9642350220003ebaaddedbc2303add1cd7 /xmake/modules | |
| parent | a33624639549e6dda946a810f175fc0e9cfc79b3 (diff) | |
| parent | 2ff05c59d4c3aa6b23b8d521d9173c0860109019 (diff) | |
Merge pull request #7145 from xmake-io/clangrt
Improve clang runtime
Diffstat (limited to 'xmake/modules')
| -rw-r--r-- | xmake/modules/core/tools/clang.lua | 121 | ||||
| -rw-r--r-- | xmake/modules/private/utils/toolchain.lua | 188 |
2 files changed, 237 insertions, 72 deletions
diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index cc9a24ca2..6b3098215 100644 --- a/xmake/modules/core/tools/clang.lua +++ b/xmake/modules/core/tools/clang.lua @@ -21,6 +21,7 @@ -- inherit gcc inherit("gcc") import("core.language.language") +import("private.utils.toolchain", {alias = "toolchain_utils"}) -- init it function init(self) @@ -191,35 +192,6 @@ function _has_static_libstdcxx(self) return has_static_libstdcxx end --- get llvm sdk root directory -function _get_llvm_rootdir(self) - local llvm_rootdir = _g._LLVM_ROOTDIR - if llvm_rootdir == nil then - local outdata = try { function() return os.iorunv(self:program(), {"-print-resource-dir"}, {envs = self:runenvs()}) end } - if outdata then - llvm_rootdir = path.normalize(path.join(outdata:trim(), "..", "..", "..")) - if not os.isdir(llvm_rootdir) then - llvm_rootdir = nil - end - end - _g._LLVM_ROOTDIR = llvm_rootdir or false - end - return llvm_rootdir or nil -end - --- get llvm target triple -function _get_llvm_target_triple(self) - local llvm_targettriple = _g._LLVM_TARGETTRIPLE - if llvm_targettriple == nil then - local outdata = try { function() return os.iorunv(self:program(), {"-print-target-triple"}, {envs = self:runenvs()}) end } - if outdata then - llvm_targettriple = outdata:trim() - end - _g._LLVM_TARGETTRIPLE = llvm_targettriple or false - end - return llvm_targettriple or nil -end - -- make the runtime flag -- @see https://github.com/xmake-io/xmake/issues/3546 function nf_runtime(self, runtime, opt) @@ -250,61 +222,66 @@ function nf_runtime(self, runtime, opt) } end end - if not self:is_plat("android") then -- we will set runtimes in android ndk toolchain + -- llvm on windows still doesn't support autolinking of libc++ and compiler-rt builtins + -- @see https://discourse.llvm.org/t/improve-autolinking-of-compiler-rt-and-libc-on-windows-with-lld-link/71392/10 + -- and need manual setting of libc++ headerdirectory + -- @see https://github.com/llvm/llvm-project/issues/79647 + local target = opt.target or opt + local llvm_dirs = toolchain_utils.get_llvm_dirs(self:toolchain()) + -- we will set runtimes in android ndk toolchain + if not self:is_plat("android") then maps = maps or {} - local llvm_rootdir = self:toolchain():sdkdir() - if kind == "cxx" then + if kind == "cxx" or kind == "ld" or kind == "sh" then maps["c++_static"] = "-stdlib=libc++" maps["c++_shared"] = "-stdlib=libc++" maps["stdc++_static"] = "-stdlib=libstdc++" maps["stdc++_shared"] = "-stdlib=libstdc++" - if not llvm_rootdir and self:is_plat("windows") then - -- clang on windows fail to add libc++ includepath when using -stdlib=libc++ so we manually add it + if kind == "cxx" then + -- force the toolchain libc++ headers to prevent clang picking the systems one -- @see https://github.com/llvm/llvm-project/issues/79647 - llvm_rootdir = _get_llvm_rootdir(self) + if llvm_dirs.cxxincludedir then + maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxincludedir) + maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxincludedir) + end + end + end + + if self:is_plat("windows") and language.sourcekinds()[kind] then + -- on windows force link to compiler_rt builtins + if llvm_dirs.rtdir and llvm_dirs.rtlink then + for name, _ in pairs(maps) do + maps[name] = table.join({"-Xclang", "--dependent-lib=" .. llvm_dirs.rtlink}, maps[name]) + end end - if llvm_rootdir then - maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. path.join(llvm_rootdir, "include", "c++", "v1")) - maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. path.join(llvm_rootdir, "include", "c++", "v1")) + end + if kind == "ld" or kind == "sh" then + if self:is_plat("windows") and llvm_dirs.rtdir then + -- on windows force add compiler_rt link directories + for name, _ in pairs(maps) do + maps[name] = table.join(nf_linkdir(self, llvm_dirs.rtdir), maps[name]) + maps[name] = table.join("-resource-dir=" .. llvm_dirs.resourcedir, maps[name]) + end end - elseif kind == "ld" or kind == "sh" then - local target = opt.target or opt local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx")) if is_cxx then - maps["c++_static"] = "-stdlib=libc++" - maps["c++_shared"] = "-stdlib=libc++" - maps["stdc++_static"] = "-stdlib=libstdc++" - maps["stdc++_shared"] = "-stdlib=libstdc++" - if not llvm_rootdir and self:is_plat("windows") then - -- clang on windows fail to add libc++ librarypath when using -stdlib=libc++ so we manually add it - -- @see https://github.com/llvm/llvm-project/issues/79647 - llvm_rootdir = _get_llvm_rootdir(self) - end - if llvm_rootdir then - local libdir = path.absolute(path.join(llvm_rootdir, "lib")) - maps["c++_static"] = table.join(maps["c++_static"], "-L" .. libdir) - maps["c++_shared"] = table.join(maps["c++_shared"], "-L" .. libdir) + if llvm_dirs.libdir then + maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.libdir)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.libdir)) + -- sometimes llvm c++ runtimes are located in c++ subfolder (e.g homebrew llvm) - local cxx_libdir = path.join(libdir, "c++") - if os.isdir(cxx_libdir) then - maps["c++_static"] = table.join(maps["c++_static"], "-L" .. cxx_libdir) - maps["c++_shared"] = table.join(maps["c++_shared"], "-L" .. cxx_libdir) - end - -- sometimes llvm runtimes are located in a target-triple subfolder - local target_triple = _get_llvm_target_triple(self) - local triple_libdir = (target_triple and os.isdir(path.join(libdir, target_triple))) and path.join(libdir, target_triple) - if triple_libdir then - maps["c++_static"] = table.join(maps["c++_static"], "-L" .. triple_libdir) - maps["c++_shared"] = table.join(maps["c++_shared"], "-L" .. triple_libdir) + if llvm_dirs.cxxlibdir then + maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlibdir)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlibdir)) end - -- add rpath to avoid the user need to set LD_LIBRARY_PATH by hand - maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, libdir)) - if triple_libdir then - maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, triple_libdir)) - end - if target.is_shared and target:is_shared() and target.filename and self:is_plat("macosx", "iphoneos", "watchos") then - maps["c++_shared"] = table.join(maps["c++_shared"], "-install_name") - maps["c++_shared"] = table.join(maps["c++_shared"], "@rpath/" .. target:filename()) + + -- add rpath to avoid the user need to set (DY)LD_LIBRARY_PATH by hand + if not self:is_plat("windows", "mingw") then + if llvm_dirs.libdir then + maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.libdir)) + end + if llvm_dirs.cxxlibdir then + maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.cxxlibdir)) + end end end if runtime:endswith("_static") and _has_static_libstdcxx(self) then diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 28cbc23c0..49af8193f 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -181,3 +181,191 @@ function map_linkflags_for_package(package, targetkind, sourcekinds, name, value return flags end +-- get llvm sdk resource directory +function get_llvm_resourcedir(toolchain) + local memcache = toolchain:memcache() + local cachekey = "get_llvm_resourcedir" + local llvm_resourcedir = memcache:get(cachekey) + if llvm_resourcedir == nil then + local cc = toolchain:tool("cc") + if cc then + local outdata = try { function() return os.iorunv(cc, {"-print-resource-dir"}) end } + if outdata then + llvm_resourcedir = path.normalize(outdata:trim()) + if not os.isdir(llvm_resourcedir) then + llvm_resourcedir = nil + end + end + end + memcache:set(cachekey, llvm_resourcedir or false) + end + return llvm_resourcedir or nil +end + +-- get llvm sdk root directory +function get_llvm_rootdir(toolchain) + local memcache = toolchain:memcache() + local cachekey = "get_llvm_rootdir" + local llvm_rootdir = memcache:get(cachekey) + if llvm_rootdir == nil then + local resourcedir = get_llvm_resourcedir(toolchain) + if resourcedir then + llvm_rootdir = path.normalize(path.join(resourcedir, "..", "..", "..")) + if not os.isdir(llvm_rootdir) then + llvm_rootdir = nil + end + end + memcache:set(cachekey, llvm_rootdir or false) + end + return llvm_rootdir or nil +end + +-- get compiler-rt info +function get_llvm_compiler_rtinfo(toolchain) + local memcache = toolchain:memcache() + local cachekey = "get_llvm_compiler_rtinfo" + local rtinfo = memcache:get(cachekey) + if rtinfo == nil then + local resourcedir = get_llvm_resourcedir(toolchain) + if resourcedir then + local res_libdir = path.join(resourcedir, "lib") + -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtins_<arch>.lib + -- when ON rtdir is windows/<target-triple> and rtlink is clang_rt.builtins.lib + local target_triple = get_llvm_target_triple(toolchain) + local arch = target_triple and target_triple:split("-")[1] + + local plat + if toolchain:is_plat("windows", "mingw") then + plat = "windows" + elseif toolchain:is_plat("linux") then + plat = "linux" + elseif toolchain:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros") then + plat = "darwin" + end + + local tripletdir = target_triple and path.join(res_libdir, "windows", target_triple) + tripletdir = os.isdir(tripletdir) or nil + + local rtdir = tripletdir and path.join(plat, target_triple) or plat + rtinfo = {rtdir = res_libdir, rtlibdir = path.join(res_libdir, rtdir)} + if os.isdir(rtinfo.rtlibdir) and toolchain:is_plat("windows", "mingw") then + local rtlink + if tripletdir then + rtlink = "clang_rt.builtins.lib" + elseif arch then + rtlink = "clang_rt.builtins-" .. arch .. ".lib" + end + if rtlink and os.isfile(path.join(rtinfo.rtlibdir, rtlink)) then + rtinfo.rtlink = path.join(rtdir, rtlink) + end + end + end + memcache:set(cachekey, rtinfo or false) + end + return rtinfo or nil +end + +-- get llvm target triple +function get_llvm_target_triple(toolchain) + local memcache = toolchain:memcache() + local cachekey = "get_llvm_target_triple" + local llvm_targettriple = memcache:get(cachekey) + if llvm_targettriple == nil then + local cc = toolchain:tool("cc") + if cc then + local outdata = try { function() return os.iorunv(cc, {"-print-target-triple"}) end } + if outdata then + llvm_targettriple = outdata:trim() + end + end + memcache:set(cachekey, llvm_targettriple or false) + end + return llvm_targettriple or nil +end + +-- get llvm toolchain dirs +function get_llvm_dirs(toolchain) + local memcache = toolchain:memcache() + local cachekey = "get_llvm_dirs" + local llvm_dirs = memcache:get(cachekey) + if llvm_dirs == nil then + local rootdir = toolchain:sdkdir() + if not rootdir and (toolchain:is_plat("windows") or is_host("windows")) then + rootdir = get_llvm_rootdir(toolchain) + end + + local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resourcedir, rtdir, rtlink, rtlibdir + if rootdir then + bindir = path.join(rootdir, "bin") + bindir = os.isdir(bindir) and bindir or nil + + libdir = path.join(rootdir, "lib") + libdir = os.isdir(libdir) and libdir or nil + + if libdir then + cxxlibdir = path.join(libdir, "c++") + cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil + if not cxxlibdir then + cxxlibdir = path.join(libdir, get_llvm_target_triple(toolchain)) + cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil + end + end + + includedir = path.join(rootdir, "include") + includedir = os.isdir(includedir) and includedir or nil + + if includedir then + cxxincludedir = path.join(includedir, "c++", "v1") + cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil + end + + resourcedir = get_llvm_resourcedir(toolchain) + local rtinfo = get_llvm_compiler_rtinfo(toolchain) + if rtinfo then + rtdir = rtinfo.rtdir + rtlink = rtinfo.rtlink + rtlibdir = rtinfo.rtlibdir + end + end + + llvm_dirs = {rootdir = rootdir, + bindir = bindir, + libdir = libdir, + cxxlibdir = cxxlibdir, + includedir = includedir, + cxxincludedir = cxxincludedir, + resourcedir = resourcedir, + rtdir = rtdir, + rtlibdir = rtlibdir, + rtlink = rtlink } + memcache:set(cachekey, llvm_dirs) + end + return llvm_dirs +end + +-- add runenvs for llvm +function add_llvm_runenvs(toolchain) + local dirs = get_llvm_dirs(toolchain) + if dirs then + if dirs.bindir and (toolchain:is_plat("windows") or is_host("windows")) then + toolchain:add("runenvs", "PATH", dirs.bindir) + end + for _, dir in ipairs({dirs.libdir or false, dirs.cxxlibdir or false, dirs.rtlibdir or false}) do + if dir then + if toolchain:is_plat("windows") or is_host("windows") then + toolchain:add("runenvs", "PATH", dir) + elseif toolchain:is_plat("linux", "bsd") then + toolchain:add("runenvs", "LD_LIBRARY_PATH", dir) + elseif toolchain:is_plat("macosx") then + -- using use DYLD_FALLBACK_LIBRARY_PATH instead of DYLD_LIBRARY_PATH to avoid symbols error when running homebrew llvm (which is linked to system libc++) + -- e.g dyld[5195]: Symbol not found: __ZnwmSt19__type_descriptor_t + -- Referenced from: <378C7CC2-7CD6-3B88-9C66-FE198E30462B> /usr/local/Cellar/llvm/21.1.5/bin/clang-21 + -- Expected as weak-def export from some loaded dylibSymbol not found: __ZnamSt19__type_descriptor_t + toolchain:add("runenvs", "DYLD_FALLBACK_LIBRARY_PATH", dir) + end + end + end + end +end + + |
