Skip to content

Commit 6b98095

Browse files
committed
fix(llvm) fix nf_runtime clang
1 parent 5e77488 commit 6b98095

File tree

6 files changed

+207
-125
lines changed

6 files changed

+207
-125
lines changed

tests/projects/c/llvm_compiler_rt/test.lua

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import("lib.detect.find_tool")
2+
import("core.tool.toolchain")
23
import("utils.ci.is_running", {alias = "ci_is_running"})
34

45
function main(t)
56
local flags = ""
67
if ci_is_running() then
7-
flags = "-vD"
8+
flags = "-vD"
89
end
910

10-
local cc = find_tool("clang", {version = true})
11-
if not cc then
12-
wprint("clang not found, skipping tests")
11+
local llvm = toolchain.load("llvm")
12+
if not llvm or not llvm:check() then
13+
wprint("llvm not found, skipping tests")
1314
return
1415
end
1516
os.exec("xmake clean -a")

xmake/modules/core/tools/clang.lua

Lines changed: 57 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
-- inherit gcc
2222
inherit("gcc")
2323
import("core.language.language")
24+
import("private.utils.toolchain", {alias = "toolchain_utils"})
2425

2526
-- init it
2627
function init(self)
@@ -191,76 +192,6 @@ function _has_static_libstdcxx(self)
191192
return has_static_libstdcxx
192193
end
193194

194-
-- get llvm sdk root directory
195-
function _get_llvm_rootdir(self)
196-
local llvm_rootdir = _g._LLVM_ROOTDIR
197-
if llvm_rootdir == nil then
198-
local outdata = try { function() return os.iorunv(self:program(), {"-print-resource-dir"}, {envs = self:runenvs()}) end }
199-
if outdata then
200-
llvm_rootdir = path.normalize(path.join(outdata:trim(), "..", "..", ".."))
201-
if not os.isdir(llvm_rootdir) then
202-
llvm_rootdir = nil
203-
end
204-
end
205-
_g._LLVM_ROOTDIR = llvm_rootdir or false
206-
end
207-
return llvm_rootdir or nil
208-
end
209-
210-
-- get llvm target triple
211-
function _get_llvm_target_triple(self)
212-
local llvm_targettriple = _g._LLVM_TARGETTRIPLE
213-
if llvm_targettriple == nil then
214-
local outdata = try { function() return os.iorunv(self:program(), {"-print-target-triple"}, {envs = self:runenvs()}) end }
215-
if outdata then
216-
llvm_targettriple = outdata:trim()
217-
end
218-
_g._LLVM_TARGETTRIPLE = llvm_targettriple or false
219-
end
220-
return llvm_targettriple or nil
221-
end
222-
223-
-- find compiler-rt dir
224-
function _get_llvm_compiler_rtdir(self, target, llvm_rootdir)
225-
import("lib.detect.find_tool")
226-
import("core.base.semver")
227-
228-
local libdir = path.absolute(path.join(llvm_rootdir, "lib", "clang"))
229-
local target_triple = _get_llvm_target_triple(self)
230-
231-
local cc = target:tool("cc")
232-
local cc_tool = find_tool(cc, {version = true})
233-
if cc_tool and cc_tool.version then
234-
local version = semver.new(cc_tool.version):major()
235-
local basedir = path.join(libdir, version, "lib")
236-
237-
local compiler_rtdir
238-
-- sometimes llvm is built with -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON, compiler_rt is located in a target-triple subfolder
239-
local tripletdir = path.join(basedir, target_triple)
240-
if os.isdir(tripletdir) then
241-
compiler_rtdir = tripletdir
242-
else
243-
-- sometimes llvm is built with -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF, compiler_rt is located in a platform name subfolder
244-
if target:is_plat("windows") then
245-
compiler_rtdir = os.isdir(path.join(basedir, "windows")) and path.join(basedir, "windows")
246-
elseif target:is_plat("linux") then
247-
compiler_rtdir = os.isdir(path.join(basedir, "linux")) and path.join(basedir, "linux")
248-
elseif target:is_plat("macosx") then
249-
compiler_rtdir = os.isdir(path.join(basedir, "darwin")) and path.join(basedir, "darwin")
250-
end
251-
end
252-
253-
if compiler_rtdir and target_triple then
254-
local arch = target_triple:split("-")[1]
255-
local compiler_rtlink = "clang_rt.builtins-" .. arch
256-
if os.isfile(path.join(compiler_rtdir, compiler_rtlink .. ".lib")) then
257-
return compiler_rtdir, path.join(compiler_rtdir, compiler_rtlink .. ".lib")
258-
end
259-
end
260-
return compiler_rtdir
261-
end
262-
end
263-
264195
-- make the runtime flag
265196
-- @see https://github.com/xmake-io/xmake/issues/3546
266197
function nf_runtime(self, runtime, opt)
@@ -291,58 +222,72 @@ function nf_runtime(self, runtime, opt)
291222
}
292223
end
293224
end
294-
if not self:is_plat("android") then -- we will set runtimes in android ndk toolchain
295-
maps = maps or {}
296-
local llvm_rootdir = self:toolchain():sdkdir()
297-
if not llvm_rootdir and self:is_plat("windows") then
298-
llvm_rootdir = _get_llvm_rootdir(self)
225+
local target = opt.target or opt
226+
-- llvm on windows still doesn't support autolinking of libc++ and compiler-rt builtins
227+
-- @see https://discourse.llvm.org/t/improve-autolinking-of-compiler-rt-and-libc-on-windows-with-lld-link/71392/10
228+
-- and need manual setting of libc++ headerdirectory
229+
-- @see https://github.com/llvm/llvm-project/issues/79647
230+
local llvm_dirs = toolchain_utils.get_llvm_dirs(self)
231+
232+
if self:is_plat("windows") and runtime == "c++_shared" then
233+
if llvm_dirs.bin then
234+
self:add("runenvs", "PATHS", llvm_dirs.bin)
299235
end
300-
if kind == "cxx" then
236+
if llvm_dirs.rt then
237+
self:add("runenvs", "PATHS", llvm_dirs.rt)
238+
end
239+
end
240+
241+
-- we will set runtimes in android ndk toolchain
242+
if not self:is_plat("android") then
243+
maps = maps or {}
244+
if kind == "cxx" or kind == "ld" or kind == "sh" then
301245
maps["c++_static"] = "-stdlib=libc++"
302246
maps["c++_shared"] = "-stdlib=libc++"
303247
maps["stdc++_static"] = "-stdlib=libstdc++"
304248
maps["stdc++_shared"] = "-stdlib=libstdc++"
305-
-- clang on windows fail to add libc++ includepath when using -stdlib=libc++ so we manually add it
306-
-- @see https://github.com/llvm/llvm-project/issues/79647
307-
if llvm_rootdir then
308-
maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. path.join(llvm_rootdir, "include", "c++", "v1"))
309-
maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. path.join(llvm_rootdir, "include", "c++", "v1"))
249+
if kind == "cxx" then
250+
-- force the toolchain libc++ headers to prevent clang picking the systems one
251+
if llvm_dirs.cxxinclude then
252+
maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxinclude)
253+
maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxinclude)
254+
end
310255
end
311-
elseif kind == "ld" or kind == "sh" then
312-
local target = opt.target or opt
313-
local is_cxx_or_c = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx", "cc"))
314-
if is_cxx_or_c then
315-
maps["c++_static"] = "-stdlib=libc++"
316-
maps["c++_shared"] = "-stdlib=libc++"
317-
maps["stdc++_static"] = "-stdlib=libstdc++"
318-
maps["stdc++_shared"] = "-stdlib=libstdc++"
319-
-- clang on windows fail to add libc++ librarypath when using -stdlib=libc++ so we manually add it
320-
-- @see https://github.com/llvm/llvm-project/issues/79647
321-
if llvm_rootdir then
322-
local libdir = path.absolute(path.join(llvm_rootdir, "lib"))
323-
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, libdir))
324-
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, libdir))
256+
end
257+
258+
if self:is_plat("windows") and language.sourcekinds()[kind] then
259+
-- on windows force link to compiler_rt builtins
260+
if llvm_dirs.rt and llvm_dirs.rtlink then
261+
for name, _ in pairs(maps) do
262+
maps[name] = table.join({"-Xclang", "--dependent-lib=" .. llvm_dirs.rtlink}, maps[name])
263+
end
264+
end
265+
end
266+
if kind == "ld" or kind == "sh" then
267+
if self:is_plat("windows") and llvm_dirs.rt then
268+
-- on windows force add compiler_rt link directories
269+
for name, _ in pairs(maps) do
270+
maps[name] = table.join(nf_linkdir(self, llvm_dirs.rt), maps[name])
271+
maps[name] = table.join("-resource-dir=" .. llvm_dirs.res, maps[name])
272+
end
273+
end
274+
275+
local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx"))
276+
if is_cxx then
277+
if llvm_dirs.lib then
278+
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.lib))
279+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.lib))
280+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.lib))
325281
-- sometimes llvm c++ runtimes are located in c++ subfolder (e.g homebrew llvm)
326-
local cxx_libdir = path.join(libdir, "c++")
327-
if os.isdir(cxx_libdir) then
328-
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, cxx_libdir))
329-
maps["c++_shared"] = table.join(maps["c++_shared"], "-L" .. nf_linkdir(self, cxx_libdir))
282+
if llvm_dirs.cxxlib then
283+
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlib))
284+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlib))
285+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.cxxlib))
330286
end
331-
local compiler_rtdir, compiler_rtlink = _get_llvm_compiler_rtdir(self, target, llvm_rootdir)
332-
if compiler_rtdir then
333-
for name, _ in pairs(maps) do
334-
maps[name] = table.join(nf_linkdir(self, compiler_rtdir), maps[name])
335-
if compiler_rtlink then
336-
maps[name] = table.join(compiler_rtlink, maps[name])
337-
end
338-
end
287+
if llvm_dirs.rt then
288+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.rt))
339289
end
340290
-- add rpath to avoid the user need to set LD_LIBRARY_PATH by hand
341-
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, libdir))
342-
if compiler_rtdir then
343-
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, compiler_rtdir))
344-
maps["MD"] = table.join(maps["MD"], nf_rpathdir(self, compiler_rtdir))
345-
end
346291
if target.is_shared and target:is_shared() and target.filename and self:is_plat("macosx", "iphoneos", "watchos") then
347292
maps["c++_shared"] = table.join(maps["c++_shared"], "-install_name")
348293
maps["c++_shared"] = table.join(maps["c++_shared"], "@rpath/" .. target:filename())

xmake/modules/private/utils/toolchain.lua

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,137 @@ function map_linkflags_for_package(package, targetkind, sourcekinds, name, value
181181
return flags
182182
end
183183

184+
-- get llvm sdk resource directory
185+
function _get_llvm_resourcedir(toolchain)
186+
local llvm_resourcedir = _g._LLVM_resourceDIR
187+
if llvm_resourcedir == nil then
188+
local outdata = try { function() return os.iorunv(toolchain:get("cc"), {"-print-resource-dir"}, {envs = toolchain:runenvs()}) end }
189+
if outdata then
190+
llvm_resourcedir = path.normalize(outdata:trim())
191+
if not os.isdir(llvm_resourcedir) then
192+
llvm_resourcedir = nil
193+
end
194+
end
195+
_g._LLVM_resourceDIR = llvm_resourcedir or false
196+
end
197+
return llvm_resourcedir or nil
198+
end
199+
200+
-- get llvm sdk root directory
201+
function _get_llvm_rootdir(self)
202+
local llvm_rootdir = _g._LLVM_ROOTDIR
203+
if llvm_rootdir == nil then
204+
local resourcedir = _get_llvm_resourcedir(self)
205+
if resourcedir then
206+
llvm_rootdir = path.normalize(path.join(resourcedir, "..", "..", ".."))
207+
if not os.isdir(llvm_rootdir) then
208+
llvm_rootdir = nil
209+
end
210+
end
211+
_g._LLVM_ROOTDIR = llvm_rootdir or false
212+
end
213+
return llvm_rootdir or nil
214+
end
215+
216+
-- find compiler-rt dir
217+
function _get_llvm_compiler_win_rtdir_and_link(self, target)
218+
import("lib.detect.find_tool")
219+
220+
local cc = self:get("cc")
221+
local cc_tool = find_tool(cc, {version = true})
222+
if cc_tool and cc_tool.version then
223+
local resdir = _get_llvm_resourcedir(self)
224+
if resdir then
225+
local res_libdir = path.join(resdir, "lib")
226+
-- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtinsi_<arch>.lib
227+
-- when ON rtdir is windows/<target-triple> and rtlink is clang_rt.builtins.lib
228+
local target_triple = _get_llvm_target_triple(self)
229+
local arch = target_triple and target_triple:split("-")[1]
230+
231+
local tripletdir = target_triple and path.join(res_libdir, "windows", target_triple)
232+
tripletdir = os.isdir(tripletdir) or nil
233+
234+
local rtdir = tripletdir and path.join("windows", target_triple) or "windows"
235+
if os.isdir(path.join(res_libdir, rtdir)) then
236+
local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib"))
237+
if os.isfile(path.join(res_libdir, rtdir, rtlink)) then
238+
return res_libdir, path.join(rtdir, rtlink)
239+
end
240+
end
241+
return res_libdir
242+
end
243+
end
244+
end
245+
246+
-- get llvm target triple
247+
function _get_llvm_target_triple(self)
248+
local llvm_targettriple = _g._LLVM_TARGETTRIPLE
249+
if llvm_targettriple == nil then
250+
local outdata = try { function() return os.iorunv(self:program(), {"-print-target-triple"}, {envs = self:runenvs()}) end }
251+
if outdata then
252+
llvm_targettriple = outdata:trim()
253+
end
254+
_g._LLVM_TARGETTRIPLE = llvm_targettriple or false
255+
end
256+
return llvm_targettriple or nil
257+
end
258+
259+
-- get llvm toolchain dirs
260+
function get_llvm_dirs(toolchain)
261+
local llvm_dirs = _g.llvm_dirs
262+
if llvm_dirs == nil then
263+
local rootdir = toolchain:sdkdir()
264+
if not rootdir and toolchain:is_plat("windows") then
265+
rootdir = _get_llvm_rootdir(toolchain)
266+
end
267+
268+
local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink
269+
if rootdir then
270+
bindir = path.join(rootdir, "bin")
271+
if bindir then
272+
bindir = os.isdir(bindir) and bindir or nil
273+
end
274+
275+
libdir = path.join(rootdir, "lib")
276+
if libdir then
277+
libdir = os.isdir(libdir) and libdir or nil
278+
end
279+
280+
if libdir then
281+
cxxlibdir = libdir and path.join(libdir, "c++")
282+
if cxxlibdir then
283+
cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil
284+
end
285+
end
286+
287+
includedir = path.join(rootdir, "include")
288+
if includedir then
289+
includedir = os.isdir(includedir) and includedir or nil
290+
end
291+
292+
if includedir then
293+
cxxincludedir = includedir and path.join(includedir, "c++", "v1") or nil
294+
if cxxincludedir then
295+
cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil
296+
end
297+
end
298+
299+
resdir = _get_llvm_resourcedir(toolchain)
300+
if toolchain:is_plat("windows") then
301+
rtdir, rtlink = _get_llvm_compiler_win_rtdir_and_link(toolchain)
302+
end
303+
end
304+
305+
llvm_dirs = {root = rootdir,
306+
bin = bindir,
307+
lib = libdir,
308+
cxxlib = cxxlibdir,
309+
include = includedir,
310+
cxxinclude = cxxincludedir,
311+
res = resdir,
312+
rt = rtdir,
313+
rtlink = rtlink }
314+
_g.llvm_dirs = llvm_dirs
315+
end
316+
return llvm_dirs
317+
end

xmake/rules/utils/compiler_runtime/xmake.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ rule("utils.compiler.runtime")
4444
-- enable vs runtime as MD by default
4545
if target:is_plat("windows") and not target:get("runtimes") then
4646
local vs_runtime_default = target:policy("build.c++.msvc.runtime")
47-
if vs_runtime_default and target:has_tool("cxx", "cl", "clang", "clang_cl") then
47+
if vs_runtime_default and target:has_tool("cxx", "cl", "clang", "clangxx", "clang_cl") then
4848
if is_mode("debug") then
4949
vs_runtime_default = vs_runtime_default .. "d"
5050
end

xmake/toolchains/llvm/check.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ function main(toolchain)
115115
toolchain:config_set("bindir", cross_toolchain.bindir)
116116
toolchain:config_set("sdkdir", cross_toolchain.sdkdir)
117117
else
118-
raise("llvm toolchain not found!")
118+
wprint("llvm toolchain not found!")
119+
return false
119120
end
120121

121122
if toolchain:is_plat("cross") and (not toolchain:cross() or toolchain:cross():match("^%s*$")) then
122-
raise("Missing cross target. Use `--cross=name` to specify.")
123+
wprint("Missing cross target. Use `--cross=name` to specify.")
124+
return false
123125
end
124126

125127
-- attempt to find xcode to pass `-isysroot` on macos

0 commit comments

Comments
 (0)