Skip to content

Commit 62bdd1f

Browse files
committed
fix(llvm) fix nf_runtime clang
1 parent 5e77488 commit 62bdd1f

File tree

4 files changed

+172
-96
lines changed

4 files changed

+172
-96
lines changed

tests/projects/c/llvm_compiler_rt/test.lua

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
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)
@@ -7,9 +8,9 @@ function main(t)
78
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: 164 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,29 @@ function _has_static_libstdcxx(self)
191191
return has_static_libstdcxx
192192
end
193193

194+
-- get llvm sdk resource directory
195+
function _get_llvm_resourcedir(self)
196+
local llvm_resourcedir = _g._LLVM_resourceDIR
197+
if llvm_resourcedir == 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_resourcedir = path.normalize(outdata:trim())
201+
if not os.isdir(llvm_resourcedir) then
202+
llvm_resourcedir = nil
203+
end
204+
end
205+
_g._LLVM_resourceDIR = llvm_resourcedir or false
206+
end
207+
return llvm_resourcedir or nil
208+
end
209+
194210
-- get llvm sdk root directory
195211
function _get_llvm_rootdir(self)
196212
local llvm_rootdir = _g._LLVM_ROOTDIR
197213
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(), "..", "..", ".."))
214+
local resourcedir = _get_llvm_resourcedir(self)
215+
if resourcedir then
216+
llvm_rootdir = path.normalize(path.join(resourcedir, "..", "..", ".."))
201217
if not os.isdir(llvm_rootdir) then
202218
llvm_rootdir = nil
203219
end
@@ -207,58 +223,107 @@ function _get_llvm_rootdir(self)
207223
return llvm_rootdir or nil
208224
end
209225

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-
223226
-- find compiler-rt dir
224-
function _get_llvm_compiler_rtdir(self, target, llvm_rootdir)
227+
function _get_llvm_compiler_win_rtdir_and_link(self, target)
225228
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)
230229

231230
local cc = target:tool("cc")
232231
local cc_tool = find_tool(cc, {version = true})
233232
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")
233+
local resdir = _get_llvm_resourcedir(self)
234+
if resdir then
235+
local res_libdir = path.join(resdir, "lib")
236+
-- when -DLLVM_ENABLE_TARGET_RUNTIME_DIR=OFF rtdir is windows/ and rtlink is clang_rt.builtinsi_<arch>.lib
237+
-- when ON rtdir is windows/<target-triple> and rtlink is clang_rt.builtins.lib
238+
local target_triple = _get_llvm_target_triple(self)
239+
local arch = target_triple and target_triple:split("-")[1]
240+
241+
local tripletdir = target_triple and path.join(res_libdir, "windows", target_triple)
242+
tripletdir = os.isdir(tripletdir) or nil
243+
244+
local rtdir = tripletdir and path.join("windows", target_triple) or "windows"
245+
if os.isdir(path.join(res_libdir, rtdir)) then
246+
local rtlink = "clang_rt.builtins" .. (tripletdir and ".lib" or ("-" .. arch .. ".lib"))
247+
if os.isfile(path.join(res_libdir, rtdir, rtlink)) then
248+
return res_libdir, path.join(rtdir, rtlink)
249+
end
250250
end
251+
return res_libdir
252+
end
253+
end
254+
end
255+
256+
-- get llvm toolchain dirs
257+
function _get_llvm_dirs(self, target)
258+
local llvm_dirs = _g.llvm_dirs
259+
if llvm_dirs == nil then
260+
local rootdir = self:toolchain():sdkdir()
261+
if not rootdir and self:is_plat("windows") then
262+
rootdir = _get_llvm_rootdir(self)
251263
end
252264

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")
265+
local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resdir, rtdir, rtlink
266+
if rootdir then
267+
bindir = path.join(rootdir, "bin")
268+
if bindir then
269+
bindir = os.isdir(bindir) and bindir or nil
270+
end
271+
272+
libdir = path.join(rootdir, "lib")
273+
if libdir then
274+
libdir = os.isdir(libdir) and libdir or nil
275+
end
276+
277+
if libdir then
278+
cxxlibdir = libdir and path.join(libdir, "c++")
279+
if cxxlibdir then
280+
cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil
281+
end
282+
end
283+
284+
includedir = path.join(rootdir, "include")
285+
if includedir then
286+
includedir = os.isdir(includedir) and includedir or nil
258287
end
288+
289+
if includedir then
290+
cxxincludedir = includedir and path.join(includedir, "c++", "v1") or nil
291+
if cxxincludedir then
292+
cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil
293+
end
294+
end
295+
296+
resdir = _get_llvm_resourcedir(self)
297+
if self:is_plat("windows") then
298+
rtdir, rtlink = _get_llvm_compiler_win_rtdir_and_link(self, target)
299+
end
300+
end
301+
302+
llvm_dirs = {root = rootdir,
303+
bin = bindir,
304+
lib = libdir,
305+
cxxlib = cxxlibdir,
306+
include = includedir,
307+
cxxinclude = cxxincludedir,
308+
res = resdir,
309+
rt = rtdir,
310+
rtlink = rtlink }
311+
_g.llvm_dirs = llvm_dirs
312+
end
313+
return llvm_dirs
314+
end
315+
316+
-- get llvm target triple
317+
function _get_llvm_target_triple(self)
318+
local llvm_targettriple = _g._LLVM_TARGETTRIPLE
319+
if llvm_targettriple == nil then
320+
local outdata = try { function() return os.iorunv(self:program(), {"-print-target-triple"}, {envs = self:runenvs()}) end }
321+
if outdata then
322+
llvm_targettriple = outdata:trim()
259323
end
260-
return compiler_rtdir
324+
_g._LLVM_TARGETTRIPLE = llvm_targettriple or false
261325
end
326+
return llvm_targettriple or nil
262327
end
263328

264329
-- make the runtime flag
@@ -291,58 +356,72 @@ function nf_runtime(self, runtime, opt)
291356
}
292357
end
293358
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)
359+
local target = opt.target or opt
360+
-- llvm on windows still doesn't support autolinking of libc++ and compiler-rt builtins
361+
-- @see https://discourse.llvm.org/t/improve-autolinking-of-compiler-rt-and-libc-on-windows-with-lld-link/71392/10
362+
-- and need manual setting of libc++ headerdirectory
363+
-- @see https://github.com/llvm/llvm-project/issues/79647
364+
local llvm_dirs = _get_llvm_dirs(self, target)
365+
366+
if self:is_plat("windows") and runtime == "c++_shared" then
367+
if llvm_dirs.bin then
368+
self:add("runenvs", "PATHS", llvm_dirs.bin)
299369
end
300-
if kind == "cxx" then
370+
if llvm_dirs.rt then
371+
self:add("runenvs", "PATHS", llvm_dirs.rt)
372+
end
373+
end
374+
375+
-- we will set runtimes in android ndk toolchain
376+
if not self:is_plat("android") then
377+
maps = maps or {}
378+
if kind == "cxx" or kind == "ld" or kind == "sh" then
301379
maps["c++_static"] = "-stdlib=libc++"
302380
maps["c++_shared"] = "-stdlib=libc++"
303381
maps["stdc++_static"] = "-stdlib=libstdc++"
304382
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"))
383+
if kind == "cxx" then
384+
-- force the toolchain libc++ headers to prevent clang picking the systems one
385+
if llvm_dirs.cxxinclude then
386+
maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxinclude)
387+
maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxinclude)
388+
end
310389
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))
390+
end
391+
392+
if self:is_plat("windows") and language.sourcekinds()[kind] then
393+
-- on windows force link to compiler_rt builtins
394+
if llvm_dirs.rt and llvm_dirs.rtlink then
395+
for name, _ in pairs(maps) do
396+
maps[name] = table.join({"-Xclang", "--dependent-lib=" .. llvm_dirs.rtlink}, maps[name])
397+
end
398+
end
399+
end
400+
if kind == "ld" or kind == "sh" then
401+
if self:is_plat("windows") and llvm_dirs.rt then
402+
-- on windows force add compiler_rt link directories
403+
for name, _ in pairs(maps) do
404+
maps[name] = table.join(nf_linkdir(self, llvm_dirs.rt), maps[name])
405+
maps[name] = table.join("-resource-dir=" .. llvm_dirs.res, maps[name])
406+
end
407+
end
408+
409+
local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx"))
410+
if is_cxx then
411+
if llvm_dirs.lib then
412+
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.lib))
413+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.lib))
414+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.lib))
325415
-- 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))
416+
if llvm_dirs.cxxlib then
417+
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlib))
418+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlib))
419+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.cxxlib))
330420
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
421+
if llvm_dirs.rt then
422+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.rt))
339423
end
340424
-- 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
346425
if target.is_shared and target:is_shared() and target.filename and self:is_plat("macosx", "iphoneos", "watchos") then
347426
maps["c++_shared"] = table.join(maps["c++_shared"], "-install_name")
348427
maps["c++_shared"] = table.join(maps["c++_shared"], "@rpath/" .. target:filename())

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

xmake/toolchains/llvm/xmake.lua

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,5 @@ toolchain("llvm")
137137
toolchain:add("shflags", "--sysroot=" .. sysroot)
138138
end
139139
end
140-
141-
-- add bin search library for loading some dependent .dll files windows
142-
local bindir = toolchain:bindir()
143-
if bindir and is_host("windows") then
144-
toolchain:add("runenvs", "PATH", bindir)
145-
end
146140
end)
147141

0 commit comments

Comments
 (0)