Skip to content

Commit dede0bc

Browse files
committed
Added context for setting/resetting env vars
1 parent 6861a02 commit dede0bc

File tree

1 file changed

+93
-102
lines changed

1 file changed

+93
-102
lines changed

easybuild/easyblocks/l/llvmcore.py

Lines changed: 93 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
@author: Kenneth Hoste (Ghent University)
3434
@author: Davide Grassano (CECAM HQ - Lausanne)
3535
"""
36+
import contextlib
3637
import glob
3738
import os
3839
import re
@@ -51,7 +52,6 @@
5152
from 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

5656
from easybuild.easyblocks.generic.cmakemake import CMakeMake
5757

@@ -99,6 +99,7 @@
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

@@ -129,6 +130,23 @@
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

133151
class 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

Comments
 (0)