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
129130 'CMAKE_VERBOSE_MAKEFILE' : 'ON' ,
130131}
131132
133+ @contextlib .contextmanager
134+ def _wrap_env (path = "" , ld_path = "" ):
135+ """Wrap the environment with the path and ld_path."""
136+ orig_path = os .getenv ('PATH' , '' )
137+ orig_ld_library_path = os .getenv ('LD_LIBRARY_PATH' , '' )
138+
139+ path = ':' .join (filter (None , [path , orig_path ]))
140+ ld_path = ':' .join (filter (None , [ld_path , orig_ld_library_path ]))
141+
142+ setvar ('PATH' , path )
143+ setvar ('LD_LIBRARY_PATH' , ld_path )
144+
145+ try :
146+ yield
147+ finally :
148+ setvar ('PATH' , orig_path )
149+ setvar ('LD_LIBRARY_PATH' , orig_ld_library_path )
132150
133151class EB_LLVMcore (CMakeMake ):
134152 """
@@ -158,7 +176,6 @@ def extra_options():
158176 'build_openmp' : [True , "Build the LLVM OpenMP runtime" , CUSTOM ],
159177 'build_openmp_tools' : [True , "Build the LLVM OpenMP tools interface" , CUSTOM ],
160178 'usepolly' : [False , "Build Clang with polly" , CUSTOM ],
161- # 'default_cuda_capability': [None, "Default CUDA capability specified for clang, e.g. '7.5'", CUSTOM],
162179 'disable_werror' : [False , "Disable -Werror for all projects" , CUSTOM ],
163180 'test_suite_max_failed' : [0 , "Maximum number of failing tests (does not count allowed failures)" , CUSTOM ],
164181 })
@@ -211,7 +228,7 @@ def __init__(self, *args, **kwargs):
211228
212229 self .full_llvm = self .cfg ['full_llvm' ]
213230
214- # Other vustom options
231+ # Other custom options
215232 if self .full_llvm :
216233 if not self .cfg ['bootstrap' ]:
217234 raise EasyBuildError ("Full LLVM build irequires bootstrap build" )
@@ -319,6 +336,7 @@ def _configure_general_build(self):
319336
320337 z3_root = get_software_root ("Z3" )
321338 if z3_root :
339+ self .log .info ("Using %s as Z3 root" , z3_root )
322340 self ._cmakeopts ['LLVM_ENABLE_Z3_SOLVER' ] = 'ON'
323341 self ._cmakeopts ['LLVM_Z3_INSTALL_DIR' ] = z3_root
324342
@@ -341,12 +359,7 @@ def _configure_final_build(self):
341359 self ._cmakeopts ['LIBOMP_HWLOC_INSTALL_DIR' ] = hwloc_root
342360
343361 if 'openmp' in self .final_projects :
344- # Disable OpenMP offload support if not building for NVPTX or AMDGPU
345362 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'
350363 self ._cmakeopts ['LIBOMP_INSTALL_ALIASES' ] = 'OFF'
351364 if not self .cfg ['build_openmp_tools' ]:
352365 self ._cmakeopts ['OPENMP_ENABLE_OMPT_TOOLS' ] = 'OFF'
@@ -363,6 +376,16 @@ def configure_step(self):
363376 """
364377 Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target
365378 """
379+ gcc_version = get_software_version ('GCCcore' )
380+ if LooseVersion (gcc_version ) < LooseVersion ('13' ):
381+ raise EasyBuildError ("LLVM %s requires GCC 13 or newer, found %s" , self .version , gcc_version )
382+
383+ # Lit is needed for running tests-suite
384+ lit_root = get_software_root ('lit' )
385+ if not lit_root :
386+ if not self .cfg ['skip_all_tests' ]:
387+ raise EasyBuildError ("Can't find `lit`, needed for running tests-suite" )
388+
366389 # Parallel build
367390 self .make_parallel_opts = ""
368391 if self .cfg ['parallel' ]:
@@ -381,22 +404,14 @@ def configure_step(self):
381404 self .log .info ("Initialising for single stage build." )
382405 self .final_dir = self .llvm_obj_dir_stage1
383406
407+ # Libxml2
384408 xml2_root = get_software_root ('libxml2' )
385409 if xml2_root :
386410 if self .full_llvm :
387411 self .log .warning ("LLVM is being built in `full_llvm` mode, libxml2 will not be used" )
388412 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 )
413+ general_opts ['LLVM_ENABLE_LIBXML2' ] = 'ON'
414+ # general_opts['LIBXML2_ROOT'] = xml2_root
400415
401416 self .llvm_obj_dir_stage1 = os .path .join (self .builddir , 'llvm.obj.1' )
402417 if self .cfg ['bootstrap' ]:
@@ -406,7 +421,6 @@ def configure_step(self):
406421 # self.final_projects.append('libc')
407422 else :
408423 self ._configure_final_build ()
409- self .final_dir = self .llvm_obj_dir_stage1
410424
411425 if self .cfg ['skip_sanitizer_tests' ] and build_option ('strict' ) != run .ERROR :
412426 self .log .debug ("Disabling the sanitizer tests" )
@@ -436,8 +450,8 @@ def configure_step(self):
436450
437451 if 'openmp' in self .final_projects :
438452 gpu_archs = []
439- gpu_archs += ['sm_%s' % cc for cc in self .cuda_cc or [] ]
440- gpu_archs += self .amd_gfx or []
453+ gpu_archs += ['sm_%s' % cc for cc in self .cuda_cc ]
454+ gpu_archs += self .amd_gfx
441455 if gpu_archs :
442456 general_opts ['LIBOMPTARGET_DEVICE_ARCHITECTURES' ] = '"%s"' % ';' .join (gpu_archs )
443457
@@ -451,9 +465,6 @@ def configure_step(self):
451465
452466 def disable_sanitizer_tests (self ):
453467 """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
457468 cmakelists_tests = os .path .join (self .llvm_src_dir , 'compiler-rt' , 'test' , 'CMakeLists.txt' )
458469 regex_subs = []
459470 regex_subs .append ((r'compiler_rt_test_runtime.*san.*' , '' ))
@@ -486,80 +497,69 @@ def configure_step3(self):
486497 def build_with_prev_stage (self , prev_dir , stage_dir ):
487498 """Build LLVM using the previous stage."""
488499 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 ()
494500
495501 bin_dir = os .path .join (prev_dir , 'bin' )
496502 lib_dir_runtime = self .get_runtime_lib_path (prev_dir , fail_ok = False )
497503
498504 # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols
499505 # e.g. when calling the compiled clang-ast-dump for stage 3
500- lib_path = ':' .join ([
501- # curr_lib_dir,
506+ lib_path = ':' .join (filter (None , [
502507 os .path .join (stage_dir , lib_dir_runtime ),
503- # prev_lib_dir,
504508 os .path .join (prev_dir , lib_dir_runtime ),
505- ])
509+ ]))
506510
507511 # 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 )
512+ with _wrap_env (bin_dir , lib_path ):
513+ # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3
514+ if build_option ('rpath' ):
515+ my_toolchain = Clang (name = 'Clang' , version = '1' )
516+ my_toolchain .prepare_rpath_wrappers (
517+ rpath_include_dirs = [
518+ os .path .join (self .installdir , 'lib' ),
519+ os .path .join (self .installdir , 'lib64' ),
520+ os .path .join (self .installdir , lib_dir_runtime ),
521+ ]
522+ )
523+ self .log .info ("Prepared rpath wrappers" )
524+
525+ # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory
526+ # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075
527+ clang_wrapper_dir = os .path .dirname (which ('clang' ))
528+ symlink (os .path .join (prev_dir , 'opt' ), os .path .join (clang_wrapper_dir , 'opt' ))
529+
530+ # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling
531+ # Clang by default warns about that, and then some configure tests use -Werror which turns those
532+ # warnings into errors. As a result, those configure tests fail, even though the compiler supports the
533+ # requested functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles
534+ # without resulting in relocation errors).
535+ # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100
536+ # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether
537+ cflags = os .getenv ('CFLAGS' , '' )
538+ cxxflags = os .getenv ('CXXFLAGS' , '' )
539+ setvar ('CFLAGS' , "%s %s" % (cflags , '-Wno-unused-command-line-argument' ))
540+ setvar ('CXXFLAGS' , "%s %s" % (cxxflags , '-Wno-unused-command-line-argument' ))
541+
542+ # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking)
543+ clang = which ('clang' )
544+ clangxx = which ('clang++' )
545+
546+ self ._cmakeopts ['CMAKE_C_COMPILER' ] = clang
547+ self ._cmakeopts ['CMAKE_CXX_COMPILER' ] = clangxx
548+ self ._cmakeopts ['CMAKE_ASM_COMPILER' ] = clang
549+ self ._cmakeopts ['CMAKE_ASM_COMPILER_ID' ] = 'Clang'
550+
551+ self .add_cmake_opts ()
552+
553+ change_dir (stage_dir )
554+ self .log .debug ("Configuring %s" , stage_dir )
555+ cmd = "cmake %s %s" % (self .cfg ['configopts' ], os .path .join (self .llvm_src_dir , 'llvm' ))
556+ run_cmd (cmd , log_all = True )
557+
558+ self .log .debug ("Building %s" , stage_dir )
559+ cmd = "make %s VERBOSE=1" % self .make_parallel_opts
560+ run_cmd (cmd , log_all = True )
559561
560562 change_dir (curdir )
561- setvar ('PATH' , orig_path )
562- setvar ('LD_LIBRARY_PATH' , orig_ld_library_path )
563563
564564 def build_step (self , verbose = False , path = None ):
565565 """Build LLVM, and optionally build it using itself."""
@@ -599,20 +599,12 @@ def _para_test_step(self, parallel=1):
599599 basedir = self .final_dir
600600
601601 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')
605602 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 )
603+ lib_path = os .path .join (basedir , lib_dir_runtime )
604+ with _wrap_env (os .path .join (basedir , 'bin' ), lib_path ):
605+ cmd = "make -j %s check-all" % parallel
606+ (out , _ ) = run_cmd (cmd , log_all = False , log_ok = False , simple = False , regexp = False )
607+ self .log .debug (out )
616608
617609 rgx_failed = re .compile (r'^ +Failed +: +([0-9]+)' , flags = re .MULTILINE )
618610 mch = rgx_failed .search (out )
@@ -718,8 +710,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F
718710
719711 # Detect OpenMP support for CPU architecture
720712 arch = get_cpu_architecture ()
721- # Check architecture explicitly since Clang uses potentially
722- # different names
713+ # Check architecture explicitly since Clang uses potentially different names
723714 if arch == X86_64 :
724715 arch = 'x86_64'
725716 elif arch == POWER :
0 commit comments