From 2a81ad148a079dd0cb42c045b8d532b61d154e50 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Fri, 31 Oct 2025 19:51:05 +0100 Subject: fix(llvm) fix llvm builtins not linked on windows --- xmake/modules/core/tools/clang.lua | 89 ++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index cc9a24ca2..820f63c0c 100644 --- a/xmake/modules/core/tools/clang.lua +++ b/xmake/modules/core/tools/clang.lua @@ -220,6 +220,47 @@ function _get_llvm_target_triple(self) return llvm_targettriple or nil end +-- find compiler-rt dir +function _get_llvm_compiler_rtdir(self, target, llvm_rootdir) + import("lib.detect.find_tool") + import("core.base.semver") + + local libdir = path.absolute(path.join(llvm_rootdir, "lib", "clang")) + local target_triple = _get_llvm_target_triple(self) + + local cc = target:tool("cc") + local cc_tool = find_tool(cc, {version = true}) + if cc_tool and cc_tool.version then + local version = semver.new(cc_tool.version):major() + local basedir = path.join(libdir, version, "lib") + + local compiler_rtdir + -- sometimes llvm is built with -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON, compiler_rt is located in a target-triple subfolder + local tripletdir = path.join(basedir, target_triple) + if os.isdir(tripletdir) then + compiler_rtdir = tripletdir + else + -- sometimes llvm is built with -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF, compiler_rt is located in a platform name subfolder + if target:is_plat("windows") then + compiler_rtdir = os.isdir(path.join(basedir, "windows")) and path.join(basedir, "windows") + elseif target:is_plat("linux") then + compiler_rtdir = os.isdir(path.join(basedir, "linux")) and path.join(basedir, "linux") + elseif target:is_plat("macosx") then + compiler_rtdir = os.isdir(path.join(basedir, "darwin")) and path.join(basedir, "darwin") + end + end + + if compiler_rtdir and target_triple then + local arch = target_triple:split("-")[1] + local compiler_rtlink = "clang_rt.builtins-" .. arch + if os.isfile(path.join(compiler_rtdir, compiler_rtlink .. ".lib")) then + return compiler_rtdir, path.join(compiler_rtdir, compiler_rtlink .. ".lib") + end + end + return compiler_rtdir + end +end + -- make the runtime flag -- @see https://github.com/xmake-io/xmake/issues/3546 function nf_runtime(self, runtime, opt) @@ -253,54 +294,54 @@ function nf_runtime(self, runtime, opt) if not self:is_plat("android") then -- we will set runtimes in android ndk toolchain maps = maps or {} local llvm_rootdir = self:toolchain():sdkdir() + if not llvm_rootdir and self:is_plat("windows") then + llvm_rootdir = _get_llvm_rootdir(self) + end if kind == "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++ includepath 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 + -- clang on windows fail to add libc++ includepath when using -stdlib=libc++ so we manually add it + -- @see https://github.com/llvm/llvm-project/issues/79647 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 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 + local is_cxx_or_c = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx", "cc")) + if is_cxx_or_c 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 + -- 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 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) + maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, libdir)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, 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) + maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, cxx_libdir)) + maps["c++_shared"] = table.join(maps["c++_shared"], "-L" .. nf_linkdir(self, 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) + local compiler_rtdir, compiler_rtlink = _get_llvm_compiler_rtdir(self, target, llvm_rootdir) + if compiler_rtdir then + for name, _ in pairs(maps) do + maps[name] = table.join(nf_linkdir(self, compiler_rtdir), maps[name]) + if compiler_rtlink then + maps[name] = table.join(compiler_rtlink, maps[name]) + end + end 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)) + if compiler_rtdir then + maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, compiler_rtdir)) + maps["MD"] = table.join(maps["MD"], nf_rpathdir(self, compiler_rtdir)) 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") -- cgit v1.3.1 From 7a1be435eef977b88a2855df9bafc4ebeca1843e Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Fri, 31 Oct 2025 19:51:35 +0100 Subject: nfc(llvm) add a test for llvm builtins --- tests/projects/c++/modules/test_base.lua | 2 +- tests/projects/c/llvm_compiler_rt/src/main.c | 5 +++++ tests/projects/c/llvm_compiler_rt/test.lua | 18 ++++++++++++++++++ tests/projects/c/llvm_compiler_rt/xmake.lua | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/projects/c/llvm_compiler_rt/src/main.c create mode 100644 tests/projects/c/llvm_compiler_rt/test.lua create mode 100644 tests/projects/c/llvm_compiler_rt/xmake.lua diff --git a/tests/projects/c++/modules/test_base.lua b/tests/projects/c++/modules/test_base.lua index 6abbeecee..0a0d07dae 100644 --- a/tests/projects/c++/modules/test_base.lua +++ b/tests/projects/c++/modules/test_base.lua @@ -103,7 +103,7 @@ function build_tests(toolchain_name, opt) end os.exec("xmake clean -a") - os.exec("xmake f" .. platform .. "--toolchain=" .. toolchain_name .. runtimes .. "-cD --yes " .. policies .. flags) + os.exec("xmake f" .. platform .. "--toolchain=" .. toolchain_name .. runtimes .. "-c --yes " .. policies .. flags) if opt.build then opt.build() else diff --git a/tests/projects/c/llvm_compiler_rt/src/main.c b/tests/projects/c/llvm_compiler_rt/src/main.c new file mode 100644 index 000000000..e12ade9ad --- /dev/null +++ b/tests/projects/c/llvm_compiler_rt/src/main.c @@ -0,0 +1,5 @@ +int main(int argc, char **argv) { + __int128 a = 123; + __int128 b = 1; + return a / b; +} diff --git a/tests/projects/c/llvm_compiler_rt/test.lua b/tests/projects/c/llvm_compiler_rt/test.lua new file mode 100644 index 000000000..3d9fd2cad --- /dev/null +++ b/tests/projects/c/llvm_compiler_rt/test.lua @@ -0,0 +1,18 @@ +import("lib.detect.find_tool") +import("utils.ci.is_running", {alias = "ci_is_running"}) + +function main(t) + local flags = "" + if ci_is_running() then + flags = "-vD" + end + + local cc = find_tool("clang", {version = true}) + if not cc then + wprint("clang not found, skipping tests") + return + end + os.exec("xmake clean -a") + os.exec("xmake f --toolchain=llvm -c --yes " .. flags) + os.run("xmake -r " .. flags) +end diff --git a/tests/projects/c/llvm_compiler_rt/xmake.lua b/tests/projects/c/llvm_compiler_rt/xmake.lua new file mode 100644 index 000000000..04267d334 --- /dev/null +++ b/tests/projects/c/llvm_compiler_rt/xmake.lua @@ -0,0 +1,3 @@ +target("foo") + set_kind("binary") + add_files("src/main.c") -- cgit v1.3.1 From 5dcd669f6e3017d6939af6fd391803c13395406a Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Tue, 4 Nov 2025 12:42:45 +0100 Subject: fix(llvm) fix nf_runtime clang --- tests/projects/c/llvm_compiler_rt/test.lua | 9 +- xmake/modules/core/tools/clang.lua | 169 +++++++++------------------ xmake/modules/private/utils/toolchain.lua | 134 +++++++++++++++++++++ xmake/rules/utils/compiler_runtime/xmake.lua | 2 +- xmake/toolchains/llvm/check.lua | 6 +- xmake/toolchains/llvm/xmake.lua | 12 +- 6 files changed, 207 insertions(+), 125 deletions(-) diff --git a/tests/projects/c/llvm_compiler_rt/test.lua b/tests/projects/c/llvm_compiler_rt/test.lua index 3d9fd2cad..b2b39f1a8 100644 --- a/tests/projects/c/llvm_compiler_rt/test.lua +++ b/tests/projects/c/llvm_compiler_rt/test.lua @@ -1,15 +1,16 @@ import("lib.detect.find_tool") +import("core.tool.toolchain") import("utils.ci.is_running", {alias = "ci_is_running"}) function main(t) local flags = "" if ci_is_running() then - flags = "-vD" + flags = "-vD" end - local cc = find_tool("clang", {version = true}) - if not cc then - wprint("clang not found, skipping tests") + local llvm = toolchain.load("llvm") + if not llvm or not llvm:check() then + wprint("llvm not found, skipping tests") return end os.exec("xmake clean -a") diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index 820f63c0c..75a165dbb 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,76 +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 - --- find compiler-rt dir -function _get_llvm_compiler_rtdir(self, target, llvm_rootdir) - import("lib.detect.find_tool") - import("core.base.semver") - - local libdir = path.absolute(path.join(llvm_rootdir, "lib", "clang")) - local target_triple = _get_llvm_target_triple(self) - - local cc = target:tool("cc") - local cc_tool = find_tool(cc, {version = true}) - if cc_tool and cc_tool.version then - local version = semver.new(cc_tool.version):major() - local basedir = path.join(libdir, version, "lib") - - local compiler_rtdir - -- sometimes llvm is built with -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON, compiler_rt is located in a target-triple subfolder - local tripletdir = path.join(basedir, target_triple) - if os.isdir(tripletdir) then - compiler_rtdir = tripletdir - else - -- sometimes llvm is built with -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF, compiler_rt is located in a platform name subfolder - if target:is_plat("windows") then - compiler_rtdir = os.isdir(path.join(basedir, "windows")) and path.join(basedir, "windows") - elseif target:is_plat("linux") then - compiler_rtdir = os.isdir(path.join(basedir, "linux")) and path.join(basedir, "linux") - elseif target:is_plat("macosx") then - compiler_rtdir = os.isdir(path.join(basedir, "darwin")) and path.join(basedir, "darwin") - end - end - - if compiler_rtdir and target_triple then - local arch = target_triple:split("-")[1] - local compiler_rtlink = "clang_rt.builtins-" .. arch - if os.isfile(path.join(compiler_rtdir, compiler_rtlink .. ".lib")) then - return compiler_rtdir, path.join(compiler_rtdir, compiler_rtlink .. ".lib") - end - end - return compiler_rtdir - end -end - -- make the runtime flag -- @see https://github.com/xmake-io/xmake/issues/3546 function nf_runtime(self, runtime, opt) @@ -291,58 +222,72 @@ function nf_runtime(self, runtime, opt) } end end - if not self:is_plat("android") then -- we will set runtimes in android ndk toolchain - maps = maps or {} - local llvm_rootdir = self:toolchain():sdkdir() - if not llvm_rootdir and self:is_plat("windows") then - llvm_rootdir = _get_llvm_rootdir(self) + local target = opt.target or opt + -- 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 llvm_dirs = toolchain_utils.get_llvm_dirs(self) + + if self:is_plat("windows") and runtime == "c++_shared" then + if llvm_dirs.bin then + self:add("runenvs", "PATHS", llvm_dirs.bin) end - if kind == "cxx" then + if llvm_dirs.rt then + self:add("runenvs", "PATHS", llvm_dirs.rt) + end + end + + -- we will set runtimes in android ndk toolchain + if not self:is_plat("android") then + maps = maps or {} + 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++" - -- clang on windows fail to add libc++ includepath when using -stdlib=libc++ so we manually add it - -- @see https://github.com/llvm/llvm-project/issues/79647 - 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")) + if kind == "cxx" then + -- force the toolchain libc++ headers to prevent clang picking the systems one + if llvm_dirs.cxxinclude then + maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxinclude) + maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxinclude) + end end - elseif kind == "ld" or kind == "sh" then - local target = opt.target or opt - local is_cxx_or_c = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx", "cc")) - if is_cxx_or_c then - maps["c++_static"] = "-stdlib=libc++" - maps["c++_shared"] = "-stdlib=libc++" - maps["stdc++_static"] = "-stdlib=libstdc++" - maps["stdc++_shared"] = "-stdlib=libstdc++" - -- 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 - if llvm_rootdir then - local libdir = path.absolute(path.join(llvm_rootdir, "lib")) - maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, libdir)) - maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, libdir)) + end + + if self:is_plat("windows") and language.sourcekinds()[kind] then + -- on windows force link to compiler_rt builtins + if llvm_dirs.rt 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 + end + if kind == "ld" or kind == "sh" then + if self:is_plat("windows") and llvm_dirs.rt then + -- on windows force add compiler_rt link directories + for name, _ in pairs(maps) do + maps[name] = table.join(nf_linkdir(self, llvm_dirs.rt), maps[name]) + maps[name] = table.join("-resource-dir=" .. llvm_dirs.res, maps[name]) + end + end + + local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx")) + if is_cxx then + if llvm_dirs.lib then + maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.lib)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.lib)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.lib)) -- 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"], nf_linkdir(self, cxx_libdir)) - maps["c++_shared"] = table.join(maps["c++_shared"], "-L" .. nf_linkdir(self, cxx_libdir)) + if llvm_dirs.cxxlib then + maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlib)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlib)) + maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.cxxlib)) end - local compiler_rtdir, compiler_rtlink = _get_llvm_compiler_rtdir(self, target, llvm_rootdir) - if compiler_rtdir then - for name, _ in pairs(maps) do - maps[name] = table.join(nf_linkdir(self, compiler_rtdir), maps[name]) - if compiler_rtlink then - maps[name] = table.join(compiler_rtlink, maps[name]) - end - end + if llvm_dirs.rt then + maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.rt)) 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 compiler_rtdir then - maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, compiler_rtdir)) - maps["MD"] = table.join(maps["MD"], nf_rpathdir(self, compiler_rtdir)) - 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()) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 28cbc23c0..551209f2d 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -181,3 +181,137 @@ function map_linkflags_for_package(package, targetkind, sourcekinds, name, value return flags end +-- get llvm sdk resource directory +function _get_llvm_resourcedir(toolchain) + local llvm_resourcedir = _g._LLVM_resourceDIR + if llvm_resourcedir == nil then + local outdata = try { function() return os.iorunv(toolchain:get("cc"), {"-print-resource-dir"}, {envs = toolchain:runenvs()}) end } + if outdata then + llvm_resourcedir = path.normalize(outdata:trim()) + if not os.isdir(llvm_resourcedir) then + llvm_resourcedir = nil + end + end + _g._LLVM_resourceDIR = llvm_resourcedir or false + end + return llvm_resourcedir or nil +end + +-- get llvm sdk root directory +function _get_llvm_rootdir(self) + local llvm_rootdir = _g._LLVM_ROOTDIR + if llvm_rootdir == nil then + local resourcedir = _get_llvm_resourcedir(self) + if resourcedir then + llvm_rootdir = path.normalize(path.join(resourcedir, "..", "..", "..")) + 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 + +-- find compiler-rt dir +function _get_llvm_compiler_win_rtdir_and_link(self, target) + import("lib.detect.find_tool") + + local cc = self:get("cc") + local cc_tool = find_tool(cc, {version = true}) + if cc_tool and cc_tool.version then + local resdir = _get_llvm_resourcedir(self) + if resdir then + local res_libdir = path.join(resdir, "lib") + -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtinsi_.lib + -- when ON rtdir is windows/ and rtlink is clang_rt.builtins.lib + local target_triple = _get_llvm_target_triple(self) + local arch = target_triple and target_triple:split("-")[1] + + local tripletdir = target_triple and path.join(res_libdir, "windows", target_triple) + tripletdir = os.isdir(tripletdir) or nil + + local rtdir = tripletdir and path.join("windows", target_triple) or "windows" + if os.isdir(path.join(res_libdir, rtdir)) then + local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) + if os.isfile(path.join(res_libdir, rtdir, rtlink)) then + return res_libdir, path.join(rtdir, rtlink) + end + end + return res_libdir + end + end +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 + +-- get llvm toolchain dirs +function get_llvm_dirs(toolchain) + local llvm_dirs = _g.llvm_dirs + if llvm_dirs == nil then + local rootdir = toolchain:sdkdir() + if not rootdir and toolchain:is_plat("windows") then + rootdir = _get_llvm_rootdir(toolchain) + end + + local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink + if rootdir then + bindir = path.join(rootdir, "bin") + if bindir then + bindir = os.isdir(bindir) and bindir or nil + end + + libdir = path.join(rootdir, "lib") + if libdir then + libdir = os.isdir(libdir) and libdir or nil + end + + if libdir then + cxxlibdir = libdir and path.join(libdir, "c++") + if cxxlibdir then + cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil + end + end + + includedir = path.join(rootdir, "include") + if includedir then + includedir = os.isdir(includedir) and includedir or nil + end + + if includedir then + cxxincludedir = includedir and path.join(includedir, "c++", "v1") or nil + if cxxincludedir then + cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil + end + end + + resdir = _get_llvm_resourcedir(toolchain) + if toolchain:is_plat("windows") then + rtdir, rtlink = _get_llvm_compiler_win_rtdir_and_link(toolchain) + end + end + + llvm_dirs = {root = rootdir, + bin = bindir, + lib = libdir, + cxxlib = cxxlibdir, + include = includedir, + cxxinclude = cxxincludedir, + res = resdir, + rt = rtdir, + rtlink = rtlink } + _g.llvm_dirs = llvm_dirs + end + return llvm_dirs +end diff --git a/xmake/rules/utils/compiler_runtime/xmake.lua b/xmake/rules/utils/compiler_runtime/xmake.lua index 6a59d7ace..756bd55f8 100644 --- a/xmake/rules/utils/compiler_runtime/xmake.lua +++ b/xmake/rules/utils/compiler_runtime/xmake.lua @@ -44,7 +44,7 @@ rule("utils.compiler.runtime") -- enable vs runtime as MD by default if target:is_plat("windows") and not target:get("runtimes") then local vs_runtime_default = target:policy("build.c++.msvc.runtime") - if vs_runtime_default and target:has_tool("cxx", "cl", "clang", "clang_cl") then + if vs_runtime_default and target:has_tool("cxx", "cl", "clang", "clangxx", "clang_cl") then if is_mode("debug") then vs_runtime_default = vs_runtime_default .. "d" end diff --git a/xmake/toolchains/llvm/check.lua b/xmake/toolchains/llvm/check.lua index 54901824a..74cc96a7f 100644 --- a/xmake/toolchains/llvm/check.lua +++ b/xmake/toolchains/llvm/check.lua @@ -115,11 +115,13 @@ function main(toolchain) toolchain:config_set("bindir", cross_toolchain.bindir) toolchain:config_set("sdkdir", cross_toolchain.sdkdir) else - raise("llvm toolchain not found!") + wprint("llvm toolchain not found!") + return false end if toolchain:is_plat("cross") and (not toolchain:cross() or toolchain:cross():match("^%s*$")) then - raise("Missing cross target. Use `--cross=name` to specify.") + wprint("Missing cross target. Use `--cross=name` to specify.") + return false end -- attempt to find xcode to pass `-isysroot` on macos diff --git a/xmake/toolchains/llvm/xmake.lua b/xmake/toolchains/llvm/xmake.lua index d4557071c..61288f1bd 100644 --- a/xmake/toolchains/llvm/xmake.lua +++ b/xmake/toolchains/llvm/xmake.lua @@ -50,12 +50,18 @@ toolchain("llvm") on_check("check") on_load(function (toolchain) + import("private.utils.toolchain", {alias = "toolchain_utils"}) -- add runtimes if toolchain:is_plat("windows") then toolchain:add("runtimes", "MT", "MTd", "MD", "MDd") end + local dirs = toolchain_utils.get_llvm_dirs(toolchain) + if dirs and dirs.rt then + toolchain:add("runenvs", dirs.rt) + end + -- add target flags local target if toolchain:is_plat("windows") and not is_host("windows") then @@ -151,11 +157,5 @@ toolchain("llvm") toolchain:add("shflags", "--sysroot=" .. sysroot) end end - - -- add bin search library for loading some dependent .dll files windows - local bindir = toolchain:bindir() - if bindir and is_host("windows") then - toolchain:add("runenvs", "PATH", bindir) - end end) -- cgit v1.3.1 From c765a2c1cac6ab126efc779eb4c823c5573dd25d Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 26 Nov 2025 15:29:03 +0100 Subject: (llvm) add runtime dirs to runenvs (for asan and libunwind) --- tests/projects/c++/modules/test_base.lua | 5 +- tests/projects/c/llvm_compiler_rt/test.lua | 7 +- xmake/modules/core/tools/clang.lua | 23 ++---- xmake/modules/private/utils/toolchain.lua | 111 ++++++++++++++++++----------- xmake/toolchains/clang/load.lua | 3 + xmake/toolchains/llvm/xmake.lua | 5 +- 6 files changed, 88 insertions(+), 66 deletions(-) diff --git a/tests/projects/c++/modules/test_base.lua b/tests/projects/c++/modules/test_base.lua index 0a0d07dae..466a59e98 100644 --- a/tests/projects/c++/modules/test_base.lua +++ b/tests/projects/c++/modules/test_base.lua @@ -11,7 +11,7 @@ MSVC_MIN_VER = "14.29" function _build(check_outdata) local flags = "" if ci_is_running() then - flags = "-vD" + flags = "-vD" end if check_outdata then local outdata @@ -101,6 +101,9 @@ function build_tests(toolchain_name, opt) if opt.flags then flags = " " .. table.concat(opt.flags, " ") end + if ci_is_running() then + flags = flags .. " -vD" + end os.exec("xmake clean -a") os.exec("xmake f" .. platform .. "--toolchain=" .. toolchain_name .. runtimes .. "-c --yes " .. policies .. flags) diff --git a/tests/projects/c/llvm_compiler_rt/test.lua b/tests/projects/c/llvm_compiler_rt/test.lua index b2b39f1a8..ee0854e3a 100644 --- a/tests/projects/c/llvm_compiler_rt/test.lua +++ b/tests/projects/c/llvm_compiler_rt/test.lua @@ -2,7 +2,7 @@ import("lib.detect.find_tool") import("core.tool.toolchain") import("utils.ci.is_running", {alias = "ci_is_running"}) -function main(t) +function run_test(toolchain_name) local flags = "" if ci_is_running() then flags = "-vD" @@ -17,3 +17,8 @@ function main(t) os.exec("xmake f --toolchain=llvm -c --yes " .. flags) os.run("xmake -r " .. flags) end + +function main(t) + run_test("llvm") + run_test("clang") +end diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index 75a165dbb..754516c59 100644 --- a/xmake/modules/core/tools/clang.lua +++ b/xmake/modules/core/tools/clang.lua @@ -227,17 +227,7 @@ function nf_runtime(self, runtime, opt) -- @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 llvm_dirs = toolchain_utils.get_llvm_dirs(self) - - if self:is_plat("windows") and runtime == "c++_shared" then - if llvm_dirs.bin then - self:add("runenvs", "PATHS", llvm_dirs.bin) - end - if llvm_dirs.rt then - self:add("runenvs", "PATHS", llvm_dirs.rt) - end - end - + 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 {} @@ -248,6 +238,7 @@ function nf_runtime(self, runtime, opt) maps["stdc++_shared"] = "-stdlib=libstdc++" 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 if llvm_dirs.cxxinclude then maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxinclude) maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxinclude) @@ -271,23 +262,19 @@ function nf_runtime(self, runtime, opt) maps[name] = table.join("-resource-dir=" .. llvm_dirs.res, maps[name]) end end - local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx")) if is_cxx then if llvm_dirs.lib then maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.lib)) maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.lib)) - maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.lib)) + -- sometimes llvm c++ runtimes are located in c++ subfolder (e.g homebrew llvm) if llvm_dirs.cxxlib then maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlib)) maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlib)) - maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.cxxlib)) end - if llvm_dirs.rt then - maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.rt)) - end - -- add rpath to avoid the user need to set LD_LIBRARY_PATH by hand + + -- add rpath to avoid the user need to set DYLD_LIBRARY_PATH by hand 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()) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 551209f2d..e1142945a 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -183,25 +183,25 @@ end -- get llvm sdk resource directory function _get_llvm_resourcedir(toolchain) - local llvm_resourcedir = _g._LLVM_resourceDIR + local llvm_resourcedir = _g._LLVM_RESOURCE_DIR if llvm_resourcedir == nil then - local outdata = try { function() return os.iorunv(toolchain:get("cc"), {"-print-resource-dir"}, {envs = toolchain:runenvs()}) end } + local outdata = try { function() return os.iorunv(toolchain:tool("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 - _g._LLVM_resourceDIR = llvm_resourcedir or false + _g._LLVM_RESOURCE_DIR = llvm_resourcedir or false end return llvm_resourcedir or nil end -- get llvm sdk root directory -function _get_llvm_rootdir(self) +function _get_llvm_rootdir(toolchain) local llvm_rootdir = _g._LLVM_ROOTDIR if llvm_rootdir == nil then - local resourcedir = _get_llvm_resourcedir(self) + local resourcedir = _get_llvm_resourcedir(toolchain) if resourcedir then llvm_rootdir = path.normalize(path.join(resourcedir, "..", "..", "..")) if not os.isdir(llvm_rootdir) then @@ -214,40 +214,49 @@ function _get_llvm_rootdir(self) end -- find compiler-rt dir -function _get_llvm_compiler_win_rtdir_and_link(self, target) +function _get_llvm_compiler_rtdir_and_link(toolchain) import("lib.detect.find_tool") - local cc = self:get("cc") + local cc = toolchain:tool("cc") local cc_tool = find_tool(cc, {version = true}) if cc_tool and cc_tool.version then - local resdir = _get_llvm_resourcedir(self) + local resdir = _get_llvm_resourcedir(toolchain) if resdir then local res_libdir = path.join(resdir, "lib") - -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtinsi_.lib + -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtins_.lib -- when ON rtdir is windows/ and rtlink is clang_rt.builtins.lib - local target_triple = _get_llvm_target_triple(self) + local target_triple = _get_llvm_target_triple(toolchain) local arch = target_triple and target_triple:split("-")[1] + local plat + if toolchain:is_plat("windows") then + plat = "windows" + elseif toolchain:is_plat("linux") then + plat = "linux" + elseif toolchain:is_plat("macosx", "ios", "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("windows", target_triple) or "windows" - if os.isdir(path.join(res_libdir, rtdir)) then + local rtdir = tripletdir and path.join(plat, target_triple) or plat + if os.isdir(path.join(res_libdir, rtdir)) and toolchain:is_plat("windows") then local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) if os.isfile(path.join(res_libdir, rtdir, rtlink)) then - return res_libdir, path.join(rtdir, rtlink) + return res_libdir, path.join(rtdir, rtlink), path.join(res_libdir, rtdir) end end - return res_libdir + return res_libdir, nil, path.join(res_libdir, rtdir) end end end -- get llvm target triple -function _get_llvm_target_triple(self) +function _get_llvm_target_triple(toolchain) 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 } + local outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-target-triple"}) end } if outdata then llvm_targettriple = outdata:trim() end @@ -268,50 +277,68 @@ function get_llvm_dirs(toolchain) local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink if rootdir then bindir = path.join(rootdir, "bin") - if bindir then - bindir = os.isdir(bindir) and bindir or nil - end + bindir = os.isdir(bindir) and bindir or nil libdir = path.join(rootdir, "lib") - if libdir then - libdir = os.isdir(libdir) and libdir or nil - end + libdir = os.isdir(libdir) and libdir or nil if libdir then - cxxlibdir = libdir and path.join(libdir, "c++") - if cxxlibdir 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") - if includedir then - includedir = os.isdir(includedir) and includedir or nil - end + includedir = os.isdir(includedir) and includedir or nil if includedir then - cxxincludedir = includedir and path.join(includedir, "c++", "v1") or nil - if cxxincludedir then - cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil - end + cxxincludedir = path.join(includedir, "c++", "v1") + cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil end resdir = _get_llvm_resourcedir(toolchain) - if toolchain:is_plat("windows") then - rtdir, rtlink = _get_llvm_compiler_win_rtdir_and_link(toolchain) - end + rtdir, rtlink, rtlib = _get_llvm_compiler_rtdir_and_link(toolchain) end llvm_dirs = {root = rootdir, - bin = bindir, - lib = libdir, - cxxlib = cxxlibdir, - include = includedir, - cxxinclude = cxxincludedir, - res = resdir, - rt = rtdir, - rtlink = rtlink } + bin = bindir, + lib = libdir, + cxxlib = cxxlibdir, + include = includedir, + cxxinclude = cxxincludedir, + res = resdir, + rt = rtdir, + rtlib = rtlib, + rtlink = rtlink } _g.llvm_dirs = llvm_dirs end return llvm_dirs end + +-- set runenvs for llvm +function set_llvm_runenvs(toolchain) + local dirs = get_llvm_dirs(toolchain) + if dirs then + if dirs.bin and toolchain:is_plat("windows") then + toolchain:add("runenvs", "PATH", dirs.bin) + end + for _, dir in ipairs({dirs.lib or false, dirs.cxxlib or false, dirs.rtlib or false}) do + if dir then + if toolchain:is_plat("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 diff --git a/xmake/toolchains/clang/load.lua b/xmake/toolchains/clang/load.lua index 74b04e867..aef891dfd 100644 --- a/xmake/toolchains/clang/load.lua +++ b/xmake/toolchains/clang/load.lua @@ -21,6 +21,7 @@ import("detect.sdks.find_vstudio") import("detect.sdks.find_mingw") import("core.project.config") +import("private.utils.toolchain", {alias = "toolchain_utils"}) -- add the given vs environment function _add_vsenv(toolchain, name, curenvs) @@ -91,6 +92,8 @@ function main(toolchain, suffix) target = target .. "-linux-gnu" end + toolchain_utils.set_llvm_runenvs(toolchain) + if target then toolchain:add("cxflags", "--target=" .. target) toolchain:add("asflags", "--target=" .. target) diff --git a/xmake/toolchains/llvm/xmake.lua b/xmake/toolchains/llvm/xmake.lua index 61288f1bd..7de57be22 100644 --- a/xmake/toolchains/llvm/xmake.lua +++ b/xmake/toolchains/llvm/xmake.lua @@ -57,10 +57,7 @@ toolchain("llvm") toolchain:add("runtimes", "MT", "MTd", "MD", "MDd") end - local dirs = toolchain_utils.get_llvm_dirs(toolchain) - if dirs and dirs.rt then - toolchain:add("runenvs", dirs.rt) - end + toolchain_utils.set_llvm_runenvs(toolchain) -- add target flags local target -- cgit v1.3.1 From c0121ce62aac7b8f37d7f2d99d7d7ad52c1fb674 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:36:39 +0800 Subject: fix conflict --- xmake/core/sandbox/modules/import/core/tool/toolchain.lua | 1 + xmake/core/tool/toolchain.lua | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/xmake/core/sandbox/modules/import/core/tool/toolchain.lua b/xmake/core/sandbox/modules/import/core/tool/toolchain.lua index 922074cac..4282acd0f 100644 --- a/xmake/core/sandbox/modules/import/core/tool/toolchain.lua +++ b/xmake/core/sandbox/modules/import/core/tool/toolchain.lua @@ -31,6 +31,7 @@ local raise = require("sandbox/modules/raise") sandbox_core_tool_toolchain.apis = toolchain.apis sandbox_core_tool_toolchain.directories = toolchain.directories sandbox_core_tool_toolchain.save = toolchain.save +sandbox_core_tool_toolchain.memcache = toolchain.memcache -- get all toolchains list function sandbox_core_tool_toolchain.list() diff --git a/xmake/core/tool/toolchain.lua b/xmake/core/tool/toolchain.lua index 1da1634db..4fff15683 100644 --- a/xmake/core/tool/toolchain.lua +++ b/xmake/core/tool/toolchain.lua @@ -567,7 +567,7 @@ function _instance:_checktool(toolkind, toolpath) end -- get memcache -function toolchain._memcache() +function toolchain.memcache() return memcache.cache("core.tool.toolchain") end @@ -732,7 +732,7 @@ function toolchain.load(name, opt) configs.arch = opt.arch or config.get("arch") or os.arch() -- get cache - local cache = toolchain._memcache() + local cache = toolchain.memcache() local cachekey = toolchain._cachekey(name, configs) -- get it directly from cache dirst @@ -797,7 +797,7 @@ function toolchain.load_withinfo(name, info, opt) configs.arch = opt.arch or config.get("arch") or os.arch() -- get cache key - local cache = toolchain._memcache() + local cache = toolchain.memcache() local cachekey = toolchain._cachekey(name, configs) -- get it directly from cache dirst @@ -909,7 +909,7 @@ function toolchain.toolconfig(toolchains, name, opt) local arch = opt.arch or config.get("arch") or os.arch() -- get cache and cachekey - local cache = toolchain._memcache() + local cache = toolchain.memcache() local cachekey = "toolconfig_" .. (opt.cachekey or "") .. "_" .. plat .. "_" .. arch -- get configuration -- cgit v1.3.1 From 23248d2ac0eda068adb3f5a97c5a1c8e1d0244c2 Mon Sep 17 00:00:00 2001 From: Arthur LAURENT Date: Wed, 26 Nov 2025 15:30:09 +0100 Subject: (llvm) use toolchain.memcache instead of global cache --- xmake/modules/private/utils/toolchain.lua | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index e1142945a..bad57b541 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -23,6 +23,7 @@ import("core.base.option") import("core.project.config") import("core.base.semver") import("core.tool.linker") +import("core.tool.toolchain", {alias = "_toolchain"}) import("core.tool.compiler") import("core.language.language") import("lib.detect.find_tool") @@ -183,7 +184,9 @@ end -- get llvm sdk resource directory function _get_llvm_resourcedir(toolchain) - local llvm_resourcedir = _g._LLVM_RESOURCE_DIR + local memcache = _toolchain.memcache() + local cachekey = toolchain:cachekey() .. "_get_llvm_resourcedir" + local llvm_resourcedir = memcache:get2(cachekey) if llvm_resourcedir == nil then local outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-resource-dir"}) end } if outdata then @@ -192,14 +195,16 @@ function _get_llvm_resourcedir(toolchain) llvm_resourcedir = nil end end - _g._LLVM_RESOURCE_DIR = llvm_resourcedir or false + 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 llvm_rootdir = _g._LLVM_ROOTDIR + local memcache = _toolchain.memcache() + local cachekey = toolchain:cachekey() .. "_get_llvm_rootdir" + local llvm_rootdir = memcache:get2(cachekey, "rootdir") if llvm_rootdir == nil then local resourcedir = _get_llvm_resourcedir(toolchain) if resourcedir then @@ -208,7 +213,7 @@ function _get_llvm_rootdir(toolchain) llvm_rootdir = nil end end - _g._LLVM_ROOTDIR = llvm_rootdir or false + memcache:set(cachekey, llvm_rootdir or false) end return llvm_rootdir or nil end @@ -254,20 +259,24 @@ end -- get llvm target triple function _get_llvm_target_triple(toolchain) - local llvm_targettriple = _g._LLVM_TARGETTRIPLE + local memcache = _toolchain.memcache() + local cachekey = toolchain:cachekey() .. "_get_llvm_target_triple" + local llvm_targettriple = memcache:get(cachekey) if llvm_targettriple == nil then local outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-target-triple"}) end } if outdata then llvm_targettriple = outdata:trim() end - _g._LLVM_TARGETTRIPLE = llvm_targettriple or false + memcache:set(cachekey, llvm_targettriple or false) end return llvm_targettriple or nil end -- get llvm toolchain dirs function get_llvm_dirs(toolchain) - local llvm_dirs = _g.llvm_dirs + local memcache = _toolchain.memcache() + local cachekey = toolchain: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") then @@ -313,6 +322,7 @@ function get_llvm_dirs(toolchain) rt = rtdir, rtlib = rtlib, rtlink = rtlink } + memcache:set(cachekey, llvm_dirs) _g.llvm_dirs = llvm_dirs end return llvm_dirs -- cgit v1.3.1 From b1a3a3a39c8a2a364af050ae29973afcec060097 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:35:14 +0800 Subject: use toolchain:memcache --- xmake/modules/private/utils/toolchain.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index bad57b541..8845dd1f1 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -23,7 +23,6 @@ import("core.base.option") import("core.project.config") import("core.base.semver") import("core.tool.linker") -import("core.tool.toolchain", {alias = "_toolchain"}) import("core.tool.compiler") import("core.language.language") import("lib.detect.find_tool") @@ -184,7 +183,7 @@ end -- get llvm sdk resource directory function _get_llvm_resourcedir(toolchain) - local memcache = _toolchain.memcache() + local memcache = toolchain:memcache() local cachekey = toolchain:cachekey() .. "_get_llvm_resourcedir" local llvm_resourcedir = memcache:get2(cachekey) if llvm_resourcedir == nil then @@ -202,7 +201,7 @@ end -- get llvm sdk root directory function _get_llvm_rootdir(toolchain) - local memcache = _toolchain.memcache() + local memcache = toolchain:memcache() local cachekey = toolchain:cachekey() .. "_get_llvm_rootdir" local llvm_rootdir = memcache:get2(cachekey, "rootdir") if llvm_rootdir == nil then @@ -259,7 +258,7 @@ end -- get llvm target triple function _get_llvm_target_triple(toolchain) - local memcache = _toolchain.memcache() + local memcache = toolchain:memcache() local cachekey = toolchain:cachekey() .. "_get_llvm_target_triple" local llvm_targettriple = memcache:get(cachekey) if llvm_targettriple == nil then @@ -274,7 +273,7 @@ end -- get llvm toolchain dirs function get_llvm_dirs(toolchain) - local memcache = _toolchain.memcache() + local memcache = toolchain:memcache() local cachekey = toolchain:cachekey() .. "_get_llvm_dirs" local llvm_dirs = memcache:get(cachekey) if llvm_dirs == nil then -- cgit v1.3.1 From dc1bd866c3fb5e851bb9f3e99ced8bf01546826c Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:37:36 +0800 Subject: fix clang test --- tests/projects/c/llvm_compiler_rt/test.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/projects/c/llvm_compiler_rt/test.lua b/tests/projects/c/llvm_compiler_rt/test.lua index ee0854e3a..bf2619e75 100644 --- a/tests/projects/c/llvm_compiler_rt/test.lua +++ b/tests/projects/c/llvm_compiler_rt/test.lua @@ -8,13 +8,13 @@ function run_test(toolchain_name) flags = "-vD" end - local llvm = toolchain.load("llvm") - if not llvm or not llvm:check() then - wprint("llvm not found, skipping tests") + local toolchain_inst = toolchain.load(toolchain_name) + if not toolchain_inst or not toolchain_inst:check() then + wprint(toolchain_name .. " not found, skipping tests") return end os.exec("xmake clean -a") - os.exec("xmake f --toolchain=llvm -c --yes " .. flags) + os.exec("xmake f --toolchain=" .. toolchain_name .. " -c --yes " .. flags) os.run("xmake -r " .. flags) end -- cgit v1.3.1 From f7120d2ab183109f53cccb5c2f7c3f477fbc5e95 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:41:17 +0800 Subject: fix toolchain memcache --- xmake/core/sandbox/modules/import/core/tool/toolchain.lua | 1 - xmake/core/tool/toolchain.lua | 8 ++++---- xmake/modules/private/utils/toolchain.lua | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/xmake/core/sandbox/modules/import/core/tool/toolchain.lua b/xmake/core/sandbox/modules/import/core/tool/toolchain.lua index 4282acd0f..922074cac 100644 --- a/xmake/core/sandbox/modules/import/core/tool/toolchain.lua +++ b/xmake/core/sandbox/modules/import/core/tool/toolchain.lua @@ -31,7 +31,6 @@ local raise = require("sandbox/modules/raise") sandbox_core_tool_toolchain.apis = toolchain.apis sandbox_core_tool_toolchain.directories = toolchain.directories sandbox_core_tool_toolchain.save = toolchain.save -sandbox_core_tool_toolchain.memcache = toolchain.memcache -- get all toolchains list function sandbox_core_tool_toolchain.list() diff --git a/xmake/core/tool/toolchain.lua b/xmake/core/tool/toolchain.lua index 4fff15683..1da1634db 100644 --- a/xmake/core/tool/toolchain.lua +++ b/xmake/core/tool/toolchain.lua @@ -567,7 +567,7 @@ function _instance:_checktool(toolkind, toolpath) end -- get memcache -function toolchain.memcache() +function toolchain._memcache() return memcache.cache("core.tool.toolchain") end @@ -732,7 +732,7 @@ function toolchain.load(name, opt) configs.arch = opt.arch or config.get("arch") or os.arch() -- get cache - local cache = toolchain.memcache() + local cache = toolchain._memcache() local cachekey = toolchain._cachekey(name, configs) -- get it directly from cache dirst @@ -797,7 +797,7 @@ function toolchain.load_withinfo(name, info, opt) configs.arch = opt.arch or config.get("arch") or os.arch() -- get cache key - local cache = toolchain.memcache() + local cache = toolchain._memcache() local cachekey = toolchain._cachekey(name, configs) -- get it directly from cache dirst @@ -909,7 +909,7 @@ function toolchain.toolconfig(toolchains, name, opt) local arch = opt.arch or config.get("arch") or os.arch() -- get cache and cachekey - local cache = toolchain.memcache() + local cache = toolchain._memcache() local cachekey = "toolconfig_" .. (opt.cachekey or "") .. "_" .. plat .. "_" .. arch -- get configuration diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 8845dd1f1..f21995ce2 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -185,7 +185,7 @@ end function _get_llvm_resourcedir(toolchain) local memcache = toolchain:memcache() local cachekey = toolchain:cachekey() .. "_get_llvm_resourcedir" - local llvm_resourcedir = memcache:get2(cachekey) + local llvm_resourcedir = memcache:get(cachekey) if llvm_resourcedir == nil then local outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-resource-dir"}) end } if outdata then @@ -203,7 +203,7 @@ end function _get_llvm_rootdir(toolchain) local memcache = toolchain:memcache() local cachekey = toolchain:cachekey() .. "_get_llvm_rootdir" - local llvm_rootdir = memcache:get2(cachekey, "rootdir") + local llvm_rootdir = memcache:get(cachekey) if llvm_rootdir == nil then local resourcedir = _get_llvm_resourcedir(toolchain) if resourcedir then -- cgit v1.3.1 From 4c04d9bda87f21357a75ace869067e7d236209f2 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:43:10 +0800 Subject: improve cachekey --- xmake/modules/private/utils/toolchain.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index f21995ce2..6e6a72c45 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -184,7 +184,7 @@ end -- get llvm sdk resource directory function _get_llvm_resourcedir(toolchain) local memcache = toolchain:memcache() - local cachekey = toolchain:cachekey() .. "_get_llvm_resourcedir" + local cachekey = "get_llvm_resourcedir" local llvm_resourcedir = memcache:get(cachekey) if llvm_resourcedir == nil then local outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-resource-dir"}) end } @@ -202,7 +202,7 @@ end -- get llvm sdk root directory function _get_llvm_rootdir(toolchain) local memcache = toolchain:memcache() - local cachekey = toolchain:cachekey() .. "_get_llvm_rootdir" + local cachekey = "get_llvm_rootdir" local llvm_rootdir = memcache:get(cachekey) if llvm_rootdir == nil then local resourcedir = _get_llvm_resourcedir(toolchain) @@ -259,7 +259,7 @@ end -- get llvm target triple function _get_llvm_target_triple(toolchain) local memcache = toolchain:memcache() - local cachekey = toolchain:cachekey() .. "_get_llvm_target_triple" + local cachekey = "get_llvm_target_triple" local llvm_targettriple = memcache:get(cachekey) if llvm_targettriple == nil then local outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-target-triple"}) end } @@ -274,7 +274,7 @@ end -- get llvm toolchain dirs function get_llvm_dirs(toolchain) local memcache = toolchain:memcache() - local cachekey = toolchain:cachekey() .. "_get_llvm_dirs" + local cachekey = "get_llvm_dirs" local llvm_dirs = memcache:get(cachekey) if llvm_dirs == nil then local rootdir = toolchain:sdkdir() -- cgit v1.3.1 From 0b8c8bf80392e56710ef760b2604fd29a185556c Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:48:17 +0800 Subject: improve toolchain for mingw --- xmake/modules/private/utils/toolchain.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 6e6a72c45..f742cf295 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -219,7 +219,6 @@ end -- find compiler-rt dir function _get_llvm_compiler_rtdir_and_link(toolchain) - import("lib.detect.find_tool") local cc = toolchain:tool("cc") local cc_tool = find_tool(cc, {version = true}) @@ -233,7 +232,7 @@ function _get_llvm_compiler_rtdir_and_link(toolchain) local arch = target_triple and target_triple:split("-")[1] local plat - if toolchain:is_plat("windows") then + if toolchain:is_plat("windows", "mingw") then plat = "windows" elseif toolchain:is_plat("linux") then plat = "linux" @@ -245,7 +244,7 @@ function _get_llvm_compiler_rtdir_and_link(toolchain) tripletdir = os.isdir(tripletdir) or nil local rtdir = tripletdir and path.join(plat, target_triple) or plat - if os.isdir(path.join(res_libdir, rtdir)) and toolchain:is_plat("windows") then + if os.isdir(path.join(res_libdir, rtdir)) and toolchain:is_plat("windows", "mingw") then local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) if os.isfile(path.join(res_libdir, rtdir, rtlink)) then return res_libdir, path.join(rtdir, rtlink), path.join(res_libdir, rtdir) @@ -278,7 +277,7 @@ function get_llvm_dirs(toolchain) local llvm_dirs = memcache:get(cachekey) if llvm_dirs == nil then local rootdir = toolchain:sdkdir() - if not rootdir and toolchain:is_plat("windows") then + if not rootdir and (toolchain:is_plat("windows") or is_host("windows")) then rootdir = _get_llvm_rootdir(toolchain) end @@ -331,12 +330,12 @@ end function set_llvm_runenvs(toolchain) local dirs = get_llvm_dirs(toolchain) if dirs then - if dirs.bin and toolchain:is_plat("windows") then + if dirs.bin and (toolchain:is_plat("windows") or is_host("windows")) then toolchain:add("runenvs", "PATH", dirs.bin) end for _, dir in ipairs({dirs.lib or false, dirs.cxxlib or false, dirs.rtlib or false}) do if dir then - if toolchain:is_plat("windows") 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) -- cgit v1.3.1 From b1e1b3e8017e37d0db6983a8a69be87d3e419002 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:56:48 +0800 Subject: add llvm rtlib --- xmake/modules/private/utils/toolchain.lua | 13 +++++++++++-- xmake/toolchains/llvm/xmake.lua | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index f742cf295..d687adf09 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -281,7 +281,7 @@ function get_llvm_dirs(toolchain) rootdir = _get_llvm_rootdir(toolchain) end - local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink + local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink, rtlib if rootdir then bindir = path.join(rootdir, "bin") bindir = os.isdir(bindir) and bindir or nil @@ -321,7 +321,6 @@ function get_llvm_dirs(toolchain) rtlib = rtlib, rtlink = rtlink } memcache:set(cachekey, llvm_dirs) - _g.llvm_dirs = llvm_dirs end return llvm_dirs end @@ -350,3 +349,13 @@ function set_llvm_runenvs(toolchain) end end end + +-- add compiler-rt for llvm +function add_llvm_compiler_rt(toolchain) + local dirs = get_llvm_dirs(toolchain) + if dirs and dirs.rtlib and dirs.rtlink then + local rtlib_path = path.join(dirs.rtlib, path.filename(dirs.rtlink)) + toolchain:add("ldflags", rtlib_path) + toolchain:add("shflags", rtlib_path) + end +end diff --git a/xmake/toolchains/llvm/xmake.lua b/xmake/toolchains/llvm/xmake.lua index 7de57be22..0c660a13a 100644 --- a/xmake/toolchains/llvm/xmake.lua +++ b/xmake/toolchains/llvm/xmake.lua @@ -58,6 +58,7 @@ toolchain("llvm") end toolchain_utils.set_llvm_runenvs(toolchain) + toolchain_utils.add_llvm_compiler_rt(toolchain) -- add target flags local target -- cgit v1.3.1 From 882f1dcc718bfa248c835f2da9b9a135e3b18cd5 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 22:59:45 +0800 Subject: fix some vars --- xmake/modules/core/tools/clang.lua | 26 +++---- xmake/modules/private/utils/toolchain.lua | 123 +++++++++++++++--------------- 2 files changed, 75 insertions(+), 74 deletions(-) diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index 754516c59..67af3da51 100644 --- a/xmake/modules/core/tools/clang.lua +++ b/xmake/modules/core/tools/clang.lua @@ -239,39 +239,39 @@ function nf_runtime(self, runtime, opt) 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 - if llvm_dirs.cxxinclude then - maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxinclude) - maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxinclude) + 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.rt and llvm_dirs.rtlink then + 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 end if kind == "ld" or kind == "sh" then - if self:is_plat("windows") and llvm_dirs.rt 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.rt), maps[name]) - maps[name] = table.join("-resource-dir=" .. llvm_dirs.res, maps[name]) + 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 local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx")) if is_cxx then - if llvm_dirs.lib then - maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.lib)) - maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.lib)) + 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) - if llvm_dirs.cxxlib then - maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlib)) - maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlib)) + 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 DYLD_LIBRARY_PATH by hand diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index d687adf09..0536c3afa 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -182,16 +182,19 @@ function map_linkflags_for_package(package, targetkind, sourcekinds, name, value end -- get llvm sdk resource directory -function _get_llvm_resourcedir(toolchain) +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 outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-resource-dir"}) end } - if outdata then - llvm_resourcedir = path.normalize(outdata:trim()) - if not os.isdir(llvm_resourcedir) then - llvm_resourcedir = nil + 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) @@ -200,12 +203,12 @@ function _get_llvm_resourcedir(toolchain) end -- get llvm sdk root directory -function _get_llvm_rootdir(toolchain) +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) + local resourcedir = get_llvm_resourcedir(toolchain) if resourcedir then llvm_rootdir = path.normalize(path.join(resourcedir, "..", "..", "..")) if not os.isdir(llvm_rootdir) then @@ -219,51 +222,49 @@ end -- find compiler-rt dir function _get_llvm_compiler_rtdir_and_link(toolchain) - - local cc = toolchain:tool("cc") - local cc_tool = find_tool(cc, {version = true}) - if cc_tool and cc_tool.version then - local resdir = _get_llvm_resourcedir(toolchain) - if resdir then - local res_libdir = path.join(resdir, "lib") - -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtins_.lib - -- when ON rtdir is windows/ 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", "ios", "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 - if os.isdir(path.join(res_libdir, rtdir)) and toolchain:is_plat("windows", "mingw") then - local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) - if os.isfile(path.join(res_libdir, rtdir, rtlink)) then - return res_libdir, path.join(rtdir, rtlink), path.join(res_libdir, rtdir) - end + local resdir = get_llvm_resourcedir(toolchain) + if resdir then + local res_libdir = path.join(resdir, "lib") + -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtins_.lib + -- when ON rtdir is windows/ 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", "ios", "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 + if os.isdir(path.join(res_libdir, rtdir)) and toolchain:is_plat("windows", "mingw") then + local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) + if os.isfile(path.join(res_libdir, rtdir, rtlink)) then + return res_libdir, path.join(rtdir, rtlink), path.join(res_libdir, rtdir) end - return res_libdir, nil, path.join(res_libdir, rtdir) end + return res_libdir, nil, path.join(res_libdir, rtdir) end end -- get llvm target triple -function _get_llvm_target_triple(toolchain) +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 outdata = try { function() return os.iorunv(toolchain:tool("cc"), {"-print-target-triple"}) end } - if outdata then - llvm_targettriple = outdata:trim() + 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 @@ -278,10 +279,10 @@ function get_llvm_dirs(toolchain) 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) + rootdir = get_llvm_rootdir(toolchain) end - local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink, rtlib + local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resourcedir, rtdir, rtlink, rtlib if rootdir then bindir = path.join(rootdir, "bin") bindir = os.isdir(bindir) and bindir or nil @@ -293,7 +294,7 @@ function get_llvm_dirs(toolchain) 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 = path.join(libdir, get_llvm_target_triple(toolchain)) cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil end end @@ -306,19 +307,19 @@ function get_llvm_dirs(toolchain) cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil end - resdir = _get_llvm_resourcedir(toolchain) + resourcedir = get_llvm_resourcedir(toolchain) rtdir, rtlink, rtlib = _get_llvm_compiler_rtdir_and_link(toolchain) end - llvm_dirs = {root = rootdir, - bin = bindir, - lib = libdir, - cxxlib = cxxlibdir, - include = includedir, - cxxinclude = cxxincludedir, - res = resdir, - rt = rtdir, - rtlib = rtlib, + llvm_dirs = {rootdir = rootdir, + bindir = bindir, + libdir = libdir, + cxxlibdir = cxxlibdir, + includedir = includedir, + cxxincludedir = cxxincludedir, + resourcedir = resourcedir, + rtdir = rtdir, + rtlibdir = rtlib, rtlink = rtlink } memcache:set(cachekey, llvm_dirs) end @@ -329,10 +330,10 @@ end function set_llvm_runenvs(toolchain) local dirs = get_llvm_dirs(toolchain) if dirs then - if dirs.bin and (toolchain:is_plat("windows") or is_host("windows")) then - toolchain:add("runenvs", "PATH", dirs.bin) + 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.lib or false, dirs.cxxlib or false, dirs.rtlib or false}) do + 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) @@ -353,8 +354,8 @@ end -- add compiler-rt for llvm function add_llvm_compiler_rt(toolchain) local dirs = get_llvm_dirs(toolchain) - if dirs and dirs.rtlib and dirs.rtlink then - local rtlib_path = path.join(dirs.rtlib, path.filename(dirs.rtlink)) + if dirs and dirs.rtlibdir and dirs.rtlink then + local rtlib_path = path.join(dirs.rtlibdir, path.filename(dirs.rtlink)) toolchain:add("ldflags", rtlib_path) toolchain:add("shflags", rtlib_path) end -- cgit v1.3.1 From 5adfdf2bb54534d8ff04bc54fb072ad3ae3e89d3 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:00:53 +0800 Subject: fix rtinfo --- xmake/modules/private/utils/toolchain.lua | 32 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 0536c3afa..3b1224230 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -220,11 +220,11 @@ function get_llvm_rootdir(toolchain) return llvm_rootdir or nil end --- find compiler-rt dir -function _get_llvm_compiler_rtdir_and_link(toolchain) - local resdir = get_llvm_resourcedir(toolchain) - if resdir then - local res_libdir = path.join(resdir, "lib") +-- get compiler-rt info +function get_llvm_compiler_rtinfo(toolchain) + 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_.lib -- when ON rtdir is windows/ and rtlink is clang_rt.builtins.lib local target_triple = get_llvm_target_triple(toolchain) @@ -235,7 +235,7 @@ function _get_llvm_compiler_rtdir_and_link(toolchain) plat = "windows" elseif toolchain:is_plat("linux") then plat = "linux" - elseif toolchain:is_plat("macosx", "ios", "watchos", "appletvos", "applexros") then + elseif toolchain:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros") then plat = "darwin" end @@ -243,13 +243,14 @@ function _get_llvm_compiler_rtdir_and_link(toolchain) tripletdir = os.isdir(tripletdir) or nil local rtdir = tripletdir and path.join(plat, target_triple) or plat - if os.isdir(path.join(res_libdir, rtdir)) and toolchain:is_plat("windows", "mingw") then + local rtinfo = {rtdir = res_libdir, rtlibdir = path.join(res_libdir, rtdir)} + if os.isdir(rtinfo.rtlibdir) and toolchain:is_plat("windows", "mingw") then local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) - if os.isfile(path.join(res_libdir, rtdir, rtlink)) then - return res_libdir, path.join(rtdir, rtlink), path.join(res_libdir, rtdir) + if os.isfile(path.join(rtinfo.rtlibdir, rtlink)) then + rtinfo.rtlink = path.join(rtdir, rtlink) end end - return res_libdir, nil, path.join(res_libdir, rtdir) + return rtinfo end end @@ -282,7 +283,7 @@ function get_llvm_dirs(toolchain) rootdir = get_llvm_rootdir(toolchain) end - local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resourcedir, rtdir, rtlink, rtlib + 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 @@ -308,7 +309,12 @@ function get_llvm_dirs(toolchain) end resourcedir = get_llvm_resourcedir(toolchain) - rtdir, rtlink, rtlib = _get_llvm_compiler_rtdir_and_link(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, @@ -319,7 +325,7 @@ function get_llvm_dirs(toolchain) cxxincludedir = cxxincludedir, resourcedir = resourcedir, rtdir = rtdir, - rtlibdir = rtlib, + rtlibdir = rtlibdir, rtlink = rtlink } memcache:set(cachekey, llvm_dirs) end -- cgit v1.3.1 From 405eecb3e6de20bdc1012ce22c0fb0afaa861bf2 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:01:55 +0800 Subject: remove rt flags --- xmake/modules/private/utils/toolchain.lua | 10 +--------- xmake/toolchains/llvm/xmake.lua | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 3b1224230..37ba7b5d3 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -357,12 +357,4 @@ function set_llvm_runenvs(toolchain) end end --- add compiler-rt for llvm -function add_llvm_compiler_rt(toolchain) - local dirs = get_llvm_dirs(toolchain) - if dirs and dirs.rtlibdir and dirs.rtlink then - local rtlib_path = path.join(dirs.rtlibdir, path.filename(dirs.rtlink)) - toolchain:add("ldflags", rtlib_path) - toolchain:add("shflags", rtlib_path) - end -end + diff --git a/xmake/toolchains/llvm/xmake.lua b/xmake/toolchains/llvm/xmake.lua index 0c660a13a..7e7a199a4 100644 --- a/xmake/toolchains/llvm/xmake.lua +++ b/xmake/toolchains/llvm/xmake.lua @@ -58,7 +58,7 @@ toolchain("llvm") end toolchain_utils.set_llvm_runenvs(toolchain) - toolchain_utils.add_llvm_compiler_rt(toolchain) + -- add target flags local target -- cgit v1.3.1 From 230d71453b663c15f40f4f67998e75dd23de96f0 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:02:44 +0800 Subject: rename to add_llvm_runenvs --- xmake/modules/private/utils/toolchain.lua | 4 ++-- xmake/toolchains/clang/load.lua | 2 +- xmake/toolchains/llvm/xmake.lua | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index 37ba7b5d3..bb7d6edba 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -332,8 +332,8 @@ function get_llvm_dirs(toolchain) return llvm_dirs end --- set runenvs for llvm -function set_llvm_runenvs(toolchain) +-- 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 diff --git a/xmake/toolchains/clang/load.lua b/xmake/toolchains/clang/load.lua index aef891dfd..3c50e3be2 100644 --- a/xmake/toolchains/clang/load.lua +++ b/xmake/toolchains/clang/load.lua @@ -92,7 +92,7 @@ function main(toolchain, suffix) target = target .. "-linux-gnu" end - toolchain_utils.set_llvm_runenvs(toolchain) + toolchain_utils.add_llvm_runenvs(toolchain) if target then toolchain:add("cxflags", "--target=" .. target) diff --git a/xmake/toolchains/llvm/xmake.lua b/xmake/toolchains/llvm/xmake.lua index 7e7a199a4..b8f37bec1 100644 --- a/xmake/toolchains/llvm/xmake.lua +++ b/xmake/toolchains/llvm/xmake.lua @@ -57,8 +57,8 @@ toolchain("llvm") toolchain:add("runtimes", "MT", "MTd", "MD", "MDd") end - toolchain_utils.set_llvm_runenvs(toolchain) - + -- add llvm runenvs + toolchain_utils.add_llvm_runenvs(toolchain) -- add target flags local target -- cgit v1.3.1 From a1c3a6c791fd97ab2e7f1d847ebd45b1f44f7dc2 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:17:46 +0800 Subject: fix check llvm on ci --- tests/projects/c/llvm_compiler_rt/test.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/projects/c/llvm_compiler_rt/test.lua b/tests/projects/c/llvm_compiler_rt/test.lua index bf2619e75..0c3481348 100644 --- a/tests/projects/c/llvm_compiler_rt/test.lua +++ b/tests/projects/c/llvm_compiler_rt/test.lua @@ -8,7 +8,12 @@ function run_test(toolchain_name) flags = "-vD" end - local toolchain_inst = toolchain.load(toolchain_name) + local plat = os.host() + local arch = os.arch() + if is_subhost("msys2") then + plat = "mingw" + end + local toolchain_inst = toolchain.load(toolchain_name, {plat = plat, arch = arch}) if not toolchain_inst or not toolchain_inst:check() then wprint(toolchain_name .. " not found, skipping tests") return -- cgit v1.3.1 From f321321bf4de7515921076412639e48a802d159f Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:17:50 +0800 Subject: fix check llvm on ci again --- tests/projects/c/llvm_compiler_rt/test.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/projects/c/llvm_compiler_rt/test.lua b/tests/projects/c/llvm_compiler_rt/test.lua index 0c3481348..6285ecb1a 100644 --- a/tests/projects/c/llvm_compiler_rt/test.lua +++ b/tests/projects/c/llvm_compiler_rt/test.lua @@ -10,7 +10,7 @@ function run_test(toolchain_name) local plat = os.host() local arch = os.arch() - if is_subhost("msys2") then + if is_subhost("msys") then plat = "mingw" end local toolchain_inst = toolchain.load(toolchain_name, {plat = plat, arch = arch}) -- cgit v1.3.1 From ae8719163477322bde3c934d73813b6b3aa3a50c Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:18:39 +0800 Subject: improve get_llvm_compiler_rtinfo --- xmake/modules/private/utils/toolchain.lua | 65 ++++++++++++++++++------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua index bb7d6edba..ffe383a5b 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -222,36 +222,47 @@ end -- get compiler-rt info function get_llvm_compiler_rtinfo(toolchain) - 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_.lib - -- when ON rtdir is windows/ 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 - local rtinfo = {rtdir = res_libdir, rtlibdir = path.join(res_libdir, rtdir)} - if os.isdir(rtinfo.rtlibdir) and toolchain:is_plat("windows", "mingw") then - local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib")) - if os.isfile(path.join(rtinfo.rtlibdir, rtlink)) then - rtinfo.rtlink = path.join(rtdir, rtlink) + 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_.lib + -- when ON rtdir is windows/ 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 - return rtinfo + memcache:set(cachekey, rtinfo or false) end + return rtinfo or nil end -- get llvm target triple -- cgit v1.3.1 From 2ff05c59d4c3aa6b23b8d521d9173c0860109019 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 18 Dec 2025 23:20:30 +0800 Subject: fix rpath for clang --- xmake/modules/core/tools/clang.lua | 16 ++++++++++------ xmake/modules/private/utils/toolchain.lua | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua index 67af3da51..6b3098215 100644 --- a/xmake/modules/core/tools/clang.lua +++ b/xmake/modules/core/tools/clang.lua @@ -222,11 +222,11 @@ function nf_runtime(self, runtime, opt) } end end - local target = opt.target or opt -- 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 + -- 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 @@ -274,10 +274,14 @@ function nf_runtime(self, runtime, opt) maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlibdir)) end - -- add rpath to avoid the user need to set DYLD_LIBRARY_PATH by hand - 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 ffe383a5b..49af8193f 100644 --- a/xmake/modules/private/utils/toolchain.lua +++ b/xmake/modules/private/utils/toolchain.lua @@ -229,7 +229,7 @@ function get_llvm_compiler_rtinfo(toolchain) 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_.lib + -- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtins_.lib -- when ON rtdir is windows/ and rtlink is clang_rt.builtins.lib local target_triple = get_llvm_target_triple(toolchain) local arch = target_triple and target_triple:split("-")[1] @@ -242,7 +242,7 @@ function get_llvm_compiler_rtinfo(toolchain) 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 -- cgit v1.3.1