@@ -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,93 @@ 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+ 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
262313end
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