@@ -191,13 +191,29 @@ function _has_static_libstdcxx(self)
191191 return has_static_libstdcxx
192192end
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
195211function _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
208224end
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
262327end
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 ())
0 commit comments