Skip to content

Commit d601b63

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

File tree

1 file changed

+95
-102
lines changed

1 file changed

+95
-102
lines changed

easybuild/easyblocks/l/llvmcore.py

Lines changed: 95 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

@@ -130,6 +131,25 @@
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+
133153
class 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

Comments
 (0)