diff options
| author | Arthur LAURENT <[email protected]> | 2025-11-04 12:42:45 +0100 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-12-18 09:31:43 +0800 |
| commit | 5dcd669f6e3017d6939af6fd391803c13395406a (patch) | |
| tree | b556d829a3e874be558ac16a29708bc321acfd6f | |
| parent | 7a1be435eef977b88a2855df9bafc4ebeca1843e (diff) | |
fix(llvm) fix nf_runtime clang
| -rw-r--r-- | tests/projects/c/llvm_compiler_rt/test.lua | 9 | ||||
| -rw-r--r-- | xmake/modules/core/tools/clang.lua | 169 | ||||
| -rw-r--r-- | xmake/modules/private/utils/toolchain.lua | 134 | ||||
| -rw-r--r-- | xmake/rules/utils/compiler_runtime/xmake.lua | 2 | ||||
| -rw-r--r-- | xmake/toolchains/llvm/check.lua | 6 | ||||
| -rw-r--r-- | 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_<arch>.lib + -- when ON rtdir is windows/<target-triple> 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) |
