3333@author: Kenneth Hoste (Ghent University)
3434@author: Davide Grassano (CECAM HQ - Lausanne)
3535"""
36+ import contextlib
3637import glob
3738import os
3839import re
5152from easybuild .tools .systemtools import (AARCH32 , AARCH64 , POWER , RISCV64 ,
5253 X86_64 , get_cpu_architecture ,
5354 get_shared_lib_ext )
54- from easybuild .tools .toolchain .toolchain import Toolchain
5555
5656from easybuild .easyblocks .generic .cmakemake import CMakeMake
5757
9999
100100 'CLANG_DEFAULT_CXX_STDLIB' : 'libc++' ,
101101 'CLANG_DEFAULT_RTLIB' : 'compiler-rt' ,
102+ # Moved to general_opts for ease of building with openmp offload (or other multi-stage builds)
102103 # 'CLANG_DEFAULT_LINKER': 'lld',
103104 'CLANG_DEFAULT_UNWINDLIB' : 'libunwind' ,
104105
130131}
131132
132133
134+ @contextlib .contextmanager
135+ def _wrap_env (path = "" , ld_path = "" ):
136+ """Wrap the environment with the path and ld_path."""
137+ orig_path = os .getenv ('PATH' , '' )
138+ orig_ld_library_path = os .getenv ('LD_LIBRARY_PATH' , '' )
139+
140+ path = ':' .join (filter (None , [path , orig_path ]))
141+ ld_path = ':' .join (filter (None , [ld_path , orig_ld_library_path ]))
142+
143+ setvar ('PATH' , path )
144+ setvar ('LD_LIBRARY_PATH' , ld_path )
145+
146+ try :
147+ yield
148+ finally :
149+ setvar ('PATH' , orig_path )
150+ setvar ('LD_LIBRARY_PATH' , orig_ld_library_path )
151+
152+
133153class EB_LLVMcore (CMakeMake ):
134154 """
135155 Support for building and installing LLVM
@@ -158,7 +178,6 @@ def extra_options():
158178 'build_openmp' : [True , "Build the LLVM OpenMP runtime" , CUSTOM ],
159179 'build_openmp_tools' : [True , "Build the LLVM OpenMP tools interface" , CUSTOM ],
160180 'usepolly' : [False , "Build Clang with polly" , CUSTOM ],
161- # 'default_cuda_capability': [None, "Default CUDA capability specified for clang, e.g. '7.5'", CUSTOM],
162181 'disable_werror' : [False , "Disable -Werror for all projects" , CUSTOM ],
163182 'test_suite_max_failed' : [0 , "Maximum number of failing tests (does not count allowed failures)" , CUSTOM ],
164183 })
@@ -211,7 +230,7 @@ def __init__(self, *args, **kwargs):
211230
212231 self .full_llvm = self .cfg ['full_llvm' ]
213232
214- # Other vustom options
233+ # Other custom options
215234 if self .full_llvm :
216235 if not self .cfg ['bootstrap' ]:
217236 raise EasyBuildError ("Full LLVM build irequires bootstrap build" )
@@ -319,6 +338,7 @@ def _configure_general_build(self):
319338
320339 z3_root = get_software_root ("Z3" )
321340 if z3_root :
341+ self .log .info ("Using %s as Z3 root" , z3_root )
322342 self ._cmakeopts ['LLVM_ENABLE_Z3_SOLVER' ] = 'ON'
323343 self ._cmakeopts ['LLVM_Z3_INSTALL_DIR' ] = z3_root
324344
@@ -341,12 +361,7 @@ def _configure_final_build(self):
341361 self ._cmakeopts ['LIBOMP_HWLOC_INSTALL_DIR' ] = hwloc_root
342362
343363 if 'openmp' in self .final_projects :
344- # Disable OpenMP offload support if not building for NVPTX or AMDGPU
345364 self ._cmakeopts ['OPENMP_ENABLE_LIBOMPTARGET' ] = 'ON'
346- # if any(target in self.build_targets for target in ['NVPTX', 'AMDGPU', 'all']):
347- # self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON'
348- # else:
349- # self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF'
350365 self ._cmakeopts ['LIBOMP_INSTALL_ALIASES' ] = 'OFF'
351366 if not self .cfg ['build_openmp_tools' ]:
352367 self ._cmakeopts ['OPENMP_ENABLE_OMPT_TOOLS' ] = 'OFF'
@@ -363,6 +378,16 @@ def configure_step(self):
363378 """
364379 Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target
365380 """
381+ gcc_version = get_software_version ('GCCcore' )
382+ if LooseVersion (gcc_version ) < LooseVersion ('13' ):
383+ raise EasyBuildError ("LLVM %s requires GCC 13 or newer, found %s" , self .version , gcc_version )
384+
385+ # Lit is needed for running tests-suite
386+ lit_root = get_software_root ('lit' )
387+ if not lit_root :
388+ if not self .cfg ['skip_all_tests' ]:
389+ raise EasyBuildError ("Can't find `lit`, needed for running tests-suite" )
390+
366391 # Parallel build
367392 self .make_parallel_opts = ""
368393 if self .cfg ['parallel' ]:
@@ -381,22 +406,14 @@ def configure_step(self):
381406 self .log .info ("Initialising for single stage build." )
382407 self .final_dir = self .llvm_obj_dir_stage1
383408
409+ # Libxml2
384410 xml2_root = get_software_root ('libxml2' )
385411 if xml2_root :
386412 if self .full_llvm :
387413 self .log .warning ("LLVM is being built in `full_llvm` mode, libxml2 will not be used" )
388414 else :
389- self ._cmakeopts ['LLVM_ENABLE_LIBXML2' ] = 'ON'
390- # self._cmakeopts['LIBXML2_ROOT'] = xml2_root
391-
392- lit_root = get_software_root ('lit' )
393- if not lit_root :
394- if not self .cfg ['skip_all_tests' ]:
395- raise EasyBuildError ("Can't find `lit`, needed for running tests-suite" )
396-
397- gcc_version = get_software_version ('GCCcore' )
398- if LooseVersion (gcc_version ) < LooseVersion ('13' ):
399- raise EasyBuildError ("LLVM %s requires GCC 13 or newer, found %s" , self .version , gcc_version )
415+ general_opts ['LLVM_ENABLE_LIBXML2' ] = 'ON'
416+ # general_opts['LIBXML2_ROOT'] = xml2_root
400417
401418 self .llvm_obj_dir_stage1 = os .path .join (self .builddir , 'llvm.obj.1' )
402419 if self .cfg ['bootstrap' ]:
@@ -406,7 +423,6 @@ def configure_step(self):
406423 # self.final_projects.append('libc')
407424 else :
408425 self ._configure_final_build ()
409- self .final_dir = self .llvm_obj_dir_stage1
410426
411427 if self .cfg ['skip_sanitizer_tests' ] and build_option ('strict' ) != run .ERROR :
412428 self .log .debug ("Disabling the sanitizer tests" )
@@ -436,8 +452,8 @@ def configure_step(self):
436452
437453 if 'openmp' in self .final_projects :
438454 gpu_archs = []
439- gpu_archs += ['sm_%s' % cc for cc in self .cuda_cc or [] ]
440- gpu_archs += self .amd_gfx or []
455+ gpu_archs += ['sm_%s' % cc for cc in self .cuda_cc ]
456+ gpu_archs += self .amd_gfx
441457 if gpu_archs :
442458 general_opts ['LIBOMPTARGET_DEVICE_ARCHITECTURES' ] = '"%s"' % ';' .join (gpu_archs )
443459
@@ -451,9 +467,6 @@ def configure_step(self):
451467
452468 def disable_sanitizer_tests (self ):
453469 """Disable the tests of all the sanitizers by removing the test directories from the build system"""
454-
455- # In Clang 3.6, the sanitizer tests are grouped together in one CMakeLists
456- # We patch out adding the subdirectories with the sanitizer tests
457470 cmakelists_tests = os .path .join (self .llvm_src_dir , 'compiler-rt' , 'test' , 'CMakeLists.txt' )
458471 regex_subs = []
459472 regex_subs .append ((r'compiler_rt_test_runtime.*san.*' , '' ))
@@ -486,80 +499,69 @@ def configure_step3(self):
486499 def build_with_prev_stage (self , prev_dir , stage_dir ):
487500 """Build LLVM using the previous stage."""
488501 curdir = os .getcwd ()
489- orig_path = os .getenv ('PATH' )
490- # orig_library_path = os.getenv('LIBRARY_PATH')
491- orig_ld_library_path = os .getenv ('LD_LIBRARY_PATH' )
492-
493- self .add_cmake_opts ()
494502
495503 bin_dir = os .path .join (prev_dir , 'bin' )
496504 lib_dir_runtime = self .get_runtime_lib_path (prev_dir , fail_ok = False )
497505
498506 # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols
499507 # e.g. when calling the compiled clang-ast-dump for stage 3
500- lib_path = ':' .join ([
501- # curr_lib_dir,
508+ lib_path = ':' .join (filter (None , [
502509 os .path .join (stage_dir , lib_dir_runtime ),
503- # prev_lib_dir,
504510 os .path .join (prev_dir , lib_dir_runtime ),
505- ])
511+ ]))
506512
507513 # Needed for passing the variables to the build command
508- setvar ('PATH' , bin_dir + ":" + orig_path )
509- setvar ('LD_LIBRARY_PATH' , lib_path + ":" + orig_ld_library_path )
510-
511- # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3
512- if build_option ('rpath' ):
513- my_toolchain = Clang (name = 'Clang' , version = '1' )
514- my_toolchain .prepare_rpath_wrappers (
515- rpath_include_dirs = [
516- os .path .join (self .installdir , 'lib' ),
517- os .path .join (self .installdir , 'lib64' ),
518- os .path .join (self .installdir , lib_dir_runtime ),
519- ]
520- )
521- self .log .info ("Prepared rpath wrappers" )
522-
523- # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory
524- # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075
525- clang_wrapper_dir = os .path .dirname (which ('clang' ))
526- symlink (os .path .join (prev_dir , 'opt' ), os .path .join (clang_wrapper_dir , 'opt' ))
527-
528- # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling
529- # Clang by default warns about that, and then some configure tests use -Werror which turns those warnings
530- # into errors. As a result, those configure tests fail, even though the compiler supports the requested
531- # functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles without
532- # resulting in relocation errors).
533- # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100
534- # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether
535- cflags = os .getenv ('CFLAGS' , '' )
536- cxxflags = os .getenv ('CXXFLAGS' , '' )
537- setvar ('CFLAGS' , "%s %s" % (cflags , '-Wno-unused-command-line-argument' ))
538- setvar ('CXXFLAGS' , "%s %s" % (cxxflags , '-Wno-unused-command-line-argument' ))
539-
540- # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking)
541- clang = which ('clang' )
542- clangxx = which ('clang++' )
543-
544- self ._cmakeopts ['CMAKE_C_COMPILER' ] = clang
545- self ._cmakeopts ['CMAKE_CXX_COMPILER' ] = clangxx
546- self ._cmakeopts ['CMAKE_ASM_COMPILER' ] = clang
547- self ._cmakeopts ['CMAKE_ASM_COMPILER_ID' ] = 'Clang'
548-
549- self .add_cmake_opts ()
550-
551- change_dir (stage_dir )
552- self .log .debug ("Configuring %s" , stage_dir )
553- cmd = "cmake %s %s" % (self .cfg ['configopts' ], os .path .join (self .llvm_src_dir , 'llvm' ))
554- run_cmd (cmd , log_all = True )
555-
556- self .log .debug ("Building %s" , stage_dir )
557- cmd = "make %s VERBOSE=1" % self .make_parallel_opts
558- run_cmd (cmd , log_all = True )
514+ with _wrap_env (bin_dir , lib_path ):
515+ # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3
516+ if build_option ('rpath' ):
517+ my_toolchain = Clang (name = 'Clang' , version = '1' )
518+ my_toolchain .prepare_rpath_wrappers (
519+ rpath_include_dirs = [
520+ os .path .join (self .installdir , 'lib' ),
521+ os .path .join (self .installdir , 'lib64' ),
522+ os .path .join (self .installdir , lib_dir_runtime ),
523+ ]
524+ )
525+ self .log .info ("Prepared rpath wrappers" )
526+
527+ # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory
528+ # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075
529+ clang_wrapper_dir = os .path .dirname (which ('clang' ))
530+ symlink (os .path .join (prev_dir , 'opt' ), os .path .join (clang_wrapper_dir , 'opt' ))
531+
532+ # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling
533+ # Clang by default warns about that, and then some configure tests use -Werror which turns those
534+ # warnings into errors. As a result, those configure tests fail, even though the compiler supports the
535+ # requested functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles
536+ # without resulting in relocation errors).
537+ # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100
538+ # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether
539+ cflags = os .getenv ('CFLAGS' , '' )
540+ cxxflags = os .getenv ('CXXFLAGS' , '' )
541+ setvar ('CFLAGS' , "%s %s" % (cflags , '-Wno-unused-command-line-argument' ))
542+ setvar ('CXXFLAGS' , "%s %s" % (cxxflags , '-Wno-unused-command-line-argument' ))
543+
544+ # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking)
545+ clang = which ('clang' )
546+ clangxx = which ('clang++' )
547+
548+ self ._cmakeopts ['CMAKE_C_COMPILER' ] = clang
549+ self ._cmakeopts ['CMAKE_CXX_COMPILER' ] = clangxx
550+ self ._cmakeopts ['CMAKE_ASM_COMPILER' ] = clang
551+ self ._cmakeopts ['CMAKE_ASM_COMPILER_ID' ] = 'Clang'
552+
553+ self .add_cmake_opts ()
554+
555+ change_dir (stage_dir )
556+ self .log .debug ("Configuring %s" , stage_dir )
557+ cmd = "cmake %s %s" % (self .cfg ['configopts' ], os .path .join (self .llvm_src_dir , 'llvm' ))
558+ run_cmd (cmd , log_all = True )
559+
560+ self .log .debug ("Building %s" , stage_dir )
561+ cmd = "make %s VERBOSE=1" % self .make_parallel_opts
562+ run_cmd (cmd , log_all = True )
559563
560564 change_dir (curdir )
561- setvar ('PATH' , orig_path )
562- setvar ('LD_LIBRARY_PATH' , orig_ld_library_path )
563565
564566 def build_step (self , verbose = False , path = None ):
565567 """Build LLVM, and optionally build it using itself."""
@@ -599,20 +601,12 @@ def _para_test_step(self, parallel=1):
599601 basedir = self .final_dir
600602
601603 change_dir (basedir )
602- orig_path = os .getenv ('PATH' )
603- orig_ld_library_path = os .getenv ('LD_LIBRARY_PATH' )
604- # lib_dir = os.path.join(basedir, 'lib')
605604 lib_dir_runtime = self .get_runtime_lib_path (basedir , fail_ok = False )
606- lib_path = ':' .join ([os .path .join (basedir , lib_dir_runtime ), orig_ld_library_path ])
607- setvar ('PATH' , os .path .join (basedir , 'bin' ) + ":" + orig_path )
608- setvar ('LD_LIBRARY_PATH' , lib_path )
609-
610- cmd = "make -j %s check-all" % parallel
611- (out , _ ) = run_cmd (cmd , log_all = False , log_ok = False , simple = False , regexp = False )
612- self .log .debug (out )
613-
614- setvar ('PATH' , orig_path )
615- setvar ('LD_LIBRARY_PATH' , orig_ld_library_path )
605+ lib_path = os .path .join (basedir , lib_dir_runtime )
606+ with _wrap_env (os .path .join (basedir , 'bin' ), lib_path ):
607+ cmd = "make -j %s check-all" % parallel
608+ (out , _ ) = run_cmd (cmd , log_all = False , log_ok = False , simple = False , regexp = False )
609+ self .log .debug (out )
616610
617611 rgx_failed = re .compile (r'^ +Failed +: +([0-9]+)' , flags = re .MULTILINE )
618612 mch = rgx_failed .search (out )
@@ -718,8 +712,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F
718712
719713 # Detect OpenMP support for CPU architecture
720714 arch = get_cpu_architecture ()
721- # Check architecture explicitly since Clang uses potentially
722- # different names
715+ # Check architecture explicitly since Clang uses potentially different names
723716 if arch == X86_64 :
724717 arch = 'x86_64'
725718 elif arch == POWER :
0 commit comments