Skip to content

Commit bd88a32

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

File tree

1 file changed

+140
-85
lines changed

1 file changed

+140
-85
lines changed

xmake/modules/core/tools/clang.lua

Lines changed: 140 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,93 @@ 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+
bindir = os.isdir(bindir) and bindir or nil
269+
270+
libdir = path.join(rootdir, "lib")
271+
libdir = os.isdir(libdir) and libdir or nil
272+
273+
cxxlibdir = libdir and path.join(libdir, "c++")
274+
cxxlibdir = os.isdir(cxxlibdir) and cxxlibdir or nil
275+
276+
includedir = path.join(rootdir, "include")
277+
includedir = os.isdir(includedir) and includedir or nil
278+
279+
cxxincludedir = includedir and path.join(includedir, "c++", "v1") or nil
280+
cxxincludedir = os.isdir(cxxincludedir) and cxxincludedir or nil
281+
282+
resdir = _get_llvm_resourcedir(self)
283+
if self:is_plat("windows") then
284+
rtdir, rtlink = _get_llvm_compiler_win_rtdir_and_link(self, target)
258285
end
259286
end
260-
return compiler_rtdir
287+
288+
llvm_dirs = {root = rootdir,
289+
bin = bindir,
290+
lib = libdir,
291+
cxxlib = cxxlibdir,
292+
include = includedir,
293+
cxxinclude = cxxincludedir,
294+
res = resdir,
295+
rt = rtdir,
296+
rtlink = rtlink }
297+
_g.llvm_dirs = llvm_dirs
298+
end
299+
return llvm_dirs
300+
end
301+
302+
-- get llvm target triple
303+
function _get_llvm_target_triple(self)
304+
local llvm_targettriple = _g._LLVM_TARGETTRIPLE
305+
if llvm_targettriple == nil then
306+
local outdata = try { function() return os.iorunv(self:program(), {"-print-target-triple"}, {envs = self:runenvs()}) end }
307+
if outdata then
308+
llvm_targettriple = outdata:trim()
309+
end
310+
_g._LLVM_TARGETTRIPLE = llvm_targettriple or false
261311
end
312+
return llvm_targettriple or nil
262313
end
263314

264315
-- make the runtime flag
@@ -291,58 +342,62 @@ function nf_runtime(self, runtime, opt)
291342
}
292343
end
293344
end
294-
if not self:is_plat("android") then -- we will set runtimes in android ndk toolchain
345+
local target = opt.target or opt
346+
-- llvm on windows still doesn't support autolinking of libc++ and compiler-rt builtins
347+
-- @see https://discourse.llvm.org/t/improve-autolinking-of-compiler-rt-and-libc-on-windows-with-lld-link/71392/10
348+
-- and need manual setting of libc++ headerdirectory
349+
-- @see https://github.com/llvm/llvm-project/issues/79647
350+
local llvm_dirs = _get_llvm_dirs(self, target)
351+
-- we will set runtimes in android ndk toolchain
352+
if not self:is_plat("android") then
295353
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)
299-
end
300-
if kind == "cxx" then
354+
if kind == "cxx" or kind == "ld" or kind == "sh" then
301355
maps["c++_static"] = "-stdlib=libc++"
302356
maps["c++_shared"] = "-stdlib=libc++"
303357
maps["stdc++_static"] = "-stdlib=libstdc++"
304358
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"))
359+
if kind == "cxx" then
360+
-- force the toolchain libc++ headers to prevent clang picking the systems one
361+
if llvm_dirs.cxxinclude then
362+
maps["c++_static"] = table.join(maps["c++_static"], "-cxx-isystem" .. llvm_dirs.cxxinclude)
363+
maps["c++_shared"] = table.join(maps["c++_shared"], "-cxx-isystem" .. llvm_dirs.cxxinclude)
364+
end
310365
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))
366+
end
367+
368+
if self:is_plat("windows") and language.sourcekinds()[kind] then
369+
-- on windows force link to compiler_rt builtins
370+
if llvm_dirs.rt and llvm_dirs.rtlink then
371+
for name, _ in pairs(maps) do
372+
maps[name] = table.join({"-Xclang", "--dependent-lib=" .. llvm_dirs.rtlink}, maps[name])
373+
end
374+
end
375+
end
376+
if kind == "ld" or kind == "sh" then
377+
if self:is_plat("windows") and llvm_dirs.rt then
378+
-- on windows force add compiler_rt link directories
379+
for name, _ in pairs(maps) do
380+
maps[name] = table.join(nf_linkdir(self, llvm_dirs.rt), maps[name])
381+
maps[name] = table.join("-resource-dir=" .. llvm_dirs.res, maps[name])
382+
end
383+
end
384+
local is_cxx = target and (target.sourcekinds and table.contains(table.wrap(target:sourcekinds()), "cxx"))
385+
if is_cxx then
386+
if llvm_dirs.lib then
387+
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.lib))
388+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.lib))
389+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.lib))
325390
-- 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))
391+
if llvm_dirs.cxxlib then
392+
maps["c++_static"] = table.join(maps["c++_static"], nf_linkdir(self, llvm_dirs.cxxlib))
393+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_linkdir(self, llvm_dirs.cxxlib))
394+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.cxxlib))
330395
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
396+
if llvm_dirs.rt then
397+
maps["c++_shared"] = table.join(maps["c++_shared"], nf_rpathdir(self, llvm_dirs.rt))
339398
end
399+
340400
-- 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
346401
if target.is_shared and target:is_shared() and target.filename and self:is_plat("macosx", "iphoneos", "watchos") then
347402
maps["c++_shared"] = table.join(maps["c++_shared"], "-install_name")
348403
maps["c++_shared"] = table.join(maps["c++_shared"], "@rpath/" .. target:filename())

0 commit comments

Comments
 (0)