From b42f6a7486d85cfa9ad14a9cd1e1265e5937c91f Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 12 Jun 2024 10:19:31 +0200 Subject: [PATCH 001/103] Added single EB for bootstrapping all LLVM components --- easybuild/easyblocks/l/llvmcore.py | 421 +++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 easybuild/easyblocks/l/llvmcore.py diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py new file mode 100644 index 00000000000..09312cbf420 --- /dev/null +++ b/easybuild/easyblocks/l/llvmcore.py @@ -0,0 +1,421 @@ +## +# Copyright 2020-2024 Ghent University +# +# This file is part of EasyBuild, +# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), +# with support of Ghent University (http://ugent.be/hpc), +# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be), +# Flemish Research Foundation (FWO) (http://www.fwo.be/en) +# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). +# +# https://github.com/easybuilders/easybuild +# +# EasyBuild is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation v2. +# +# EasyBuild is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with EasyBuild. If not, see . +## +""" +EasyBuild support for building and installing LLVM, implemented as an easyblock + +@author: Simon Branford (University of Birmingham) +@author: Kenneth Hoste (Ghent University) +@author: Davide Grassano (CECAM HQ - Lausanne) +""" +import glob +import os +import re +import shutil + +from easybuild.framework.easyconfig import CUSTOM +from easybuild.tools import LooseVersion, run +from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning +from easybuild.tools.config import build_option +from easybuild.tools.environment import setvar +from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, + mkdir) +from easybuild.tools.modules import get_software_root, get_software_version +from easybuild.tools.run import run_cmd +from easybuild.tools.systemtools import (get_cpu_architecture, + get_shared_lib_ext) + +from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP +from easybuild.easyblocks.generic.cmakemake import CMakeMake + +cmake_opt_post3 = { + 'LIBCXX_CXX_ABI': 'libcxxabi', + 'LIBCXX_USE_COMPILER_RT': 'On', + 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', + 'LIBCXXABI_USE_COMPILER_RT': 'On', + 'LIBCXX_HAS_GCC_S_LIB': 'Off', + 'LIBUNWIND_USE_COMPILER_RT': 'On', + 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', + 'CLANG_DEFAULT_RTLIB': 'compiler-rt', + 'CLANG_DEFAULT_LINKER': 'lld', + 'LLVM_POLLY_LINK_INTO_TOOLS': 'ON', +} + +class EB_LLVMcore(CMakeMake): + """ + Support for building and installing LLVM + """ + + @staticmethod + def extra_options(): + extra_vars = CMakeMake.extra_options() + extra_vars.update({ + 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], + 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + + ', '.join(CLANG_TARGETS), CUSTOM], + 'bootstrap': [True, "Build LLVM using itself", CUSTOM], + 'enable_rtti': [True, "Enable RTTI", CUSTOM], + 'skip_all_tests': [False, "Skip running of tests", CUSTOM], + 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], + 'python_bindings': [False, "Install python bindings", CUSTOM], + 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], + }) + + return extra_vars + + def __init__(self, *args, **kwargs): + """Initialize LLVM-specific variables.""" + super(EB_LLVMcore, self).__init__(*args, **kwargs) + self.llvm_src_dir = None + self.llvm_obj_dir_stage1 = None + self.llvm_obj_dir_stage2 = None + self.llvm_obj_dir_stage3 = None + # self.llvm_obj_dir_stage4 = None + self.make_parallel_opts = "" + + if LooseVersion(self.version) < LooseVersion('18.1.6'): + raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) + + self.build_shared = self.cfg.get('build_shared_libs', False) + # self.cfg['start_dir'] = 'llvm' + if self.build_shared: + self.cfg['build_shared_libs'] = None + + # self._projects = ['llvm'] + # self._runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + self._cmakeopts = { + 'LLVM_ENABLE_PROJECTS': 'llvm;lld;lldb;polly;mlir', + 'LLVM_ENABLE_RUNTIMES': 'compiler-rt;libunwind;libcxx;libcxxabi', + } + self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) + + def _general_configure_step(self): + """General configuration step for LLVM.""" + self._cmakeopts['CMAKE_BUILD_TYPE'] = self.build_type + # If EB is launched from a venv, avoid giving priority to the venv's python + self._cmakeopts['Python3_FIND_VIRTUALENV'] = 'STANDARD' + self._cmakeopts['LLVM_INSTALL_UTILS'] = 'ON' + self._cmakeopts['LLVM_INCLUDE_BENCHMARKS'] = 'OFF' + self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' + + if self.build_shared: + self.cfg.update('configopts', '-DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON') + + if get_software_root('zlib'): + self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' + + if self.cfg["enable_rtti"]: + self._cmakeopts['LLVM_REQUIRES_RTTI'] = 'ON' + self._cmakeopts['LLVM_ENABLE_RTTI'] = 'ON' + # self._cmakeopts['LLVM_ENABLE_EH'] = 'ON' + + def configure_step(self): + """ + Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target + """ + gcc_version = get_software_version('GCCcore') + if LooseVersion(gcc_version) < LooseVersion('13'): + raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) + + if self.cfg['parallel']: + self.make_parallel_opts = "-j %s" % self.cfg['parallel'] + + self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') + if self.cfg['bootstrap']: + self.log.info("Initialising for bootstrap build.") + self.llvm_obj_dir_stage2 = os.path.join(self.builddir, 'llvm.obj.2') + self.llvm_obj_dir_stage3 = os.path.join(self.builddir, 'llvm.obj.3') + # self.llvm_obj_dir_stage4 = os.path.join(self.builddir, 'llvm.obj.4') + mkdir(self.llvm_obj_dir_stage2) + mkdir(self.llvm_obj_dir_stage3) + # mkdir(self.llvm_obj_dir_stage4) + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' + + if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: + self.log.debug("Disabling the sanitizer tests") + self.disable_sanitizer_tests() + + gcc_prefix = get_software_root('GCCcore') + # If that doesn't work, try with GCC + if gcc_prefix is None: + gcc_prefix = get_software_root('GCC') + # If that doesn't work either, print error and exit + if gcc_prefix is None: + raise EasyBuildError("Can't find GCC or GCCcore to use") + self._cmakeopts['GCC_INSTALL_PREFIX'] = gcc_prefix + self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) + + self._general_configure_step() + + build_targets = self.cfg['build_targets'] + if build_targets is None: + arch = get_cpu_architecture() + try: + default_targets = DEFAULT_TARGETS_MAP[arch][:] + self.cfg['build_targets'] = build_targets = default_targets + self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) + except KeyError: + raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) + + unknown_targets = [target for target in build_targets if target not in CLANG_TARGETS] + + if unknown_targets: + raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", + ', '.join(unknown_targets), ', '.join(CLANG_TARGETS)) + + self.cfg.update('configopts', '-DLLVM_TARGETS_TO_BUILD="%s"' % ';'.join(build_targets)) + + self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) + + self.add_cmake_opts() + super(EB_LLVMcore, self).configure_step( + builddir=self.llvm_obj_dir_stage1, + srcdir=os.path.join(self.llvm_src_dir, "llvm") + ) + + def disable_sanitizer_tests(self): + """Disable the tests of all the sanitizers by removing the test directories from the build system""" + + # In Clang 3.6, the sanitizer tests are grouped together in one CMakeLists + # We patch out adding the subdirectories with the sanitizer tests + cmakelists_tests = os.path.join(self.llvm_src_dir, 'compiler-rt', 'test', 'CMakeLists.txt') + regex_subs = [] + regex_subs.append((r'compiler_rt_test_runtime.*san.*', '')) + + apply_regex_substitutions(cmakelists_tests, regex_subs) + + def add_cmake_opts(self): + """Add LLVM-specific CMake options.""" + base_opts = self._cfgopts.copy() + for k,v in self._cmakeopts.items(): + base_opts.append('-D%s=%s' % (k, v)) + self.cfg['configopts'] = ' '.join(base_opts) + + self.log.debug("-%"*50) + self.log.debug("Using %s as configopts", self._cfgopts) + self.log.debug("Using %s as cmakeopts", self._cmakeopts) + self.log.debug("-%"*50) + + def configure_step2(self): + """Configure the second stage of the bootstrap.""" + self._cmakeopts = {} + self._general_configure_step() + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' + + # def configure_step3(self): + # """Configure the second stage of the bootstrap.""" + # self._cmakeopts = {} + # self._general_configure_step() + # self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' + # self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' + # self._cmakeopts.update(cmake_opt_post3) + + def configure_step3(self): + """Configure the third stage of the bootstrap.""" + self._cmakeopts = {} + self._general_configure_step() + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;lldb;mlir;polly;clang;flang"' + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' + self._cmakeopts.update(cmake_opt_post3) + + def build_with_prev_stage(self, prev_dir, stage_dir): + """Build LLVM using the previous stage.""" + curdir = os.getcwd() + orig_path = os.getenv('PATH') + orig_library_path = os.getenv('LIBRARY_PATH') + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + + self._cmakeopts['CMAKE_C_COMPILER'] = os.path.join(prev_dir, 'bin/clang') + self._cmakeopts['CMAKE_CXX_COMPILER'] = os.path.join(prev_dir, 'bin/clang++') + # self._cmakeopts['CMAKE_ASM_COMPILER'] = os.path.join(prev_dir, 'bin/clang') + + self.add_cmake_opts() + + bin_dir = os.path.join(prev_dir, 'bin') + prev_lib_dir = os.path.join(prev_dir, 'lib') + curr_lib_dir = os.path.join(stage_dir, 'lib') + lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) + + # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols + # e.g. when calling the compiled clang-ast-dump for stage 3 + lib_path = ':'.join([ + curr_lib_dir, + os.path.join(curr_lib_dir, lib_dir_runtime), + prev_lib_dir, + os.path.join(prev_dir, lib_dir_runtime), + ]) + + # setvar('PATH', bin_dir + ":" + orig_path) + # setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) + # setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) + + self.cfg.update('preconfigopts', ' '.join([ + 'PATH=%s:%s' % (bin_dir, orig_path), + 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), + 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) + ])) + super(EB_LLVMcore, self).configure_step( + builddir=stage_dir, + srcdir=os.path.join(self.llvm_src_dir, "llvm") + ) + + # change_dir(stage_dir) + # self.log.debug("Configuring %s", stage_dir) + # cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) + # run_cmd(cmd, log_all=True) + self.log.debug("Building %s", stage_dir) + cmd = "make %s VERBOSE=1" % self.make_parallel_opts + run_cmd(cmd, log_all=True) + + change_dir(curdir) + # setvar('PATH', orig_path) + # setvar('LIBRARY_PATH', orig_library_path) + # setvar('LD_LIBRARY_PATH', orig_ld_library_path) + + def build_step(self, verbose=False, path=None): + """Build LLVM, and optionally build it using itself.""" + self.log.info("Building stage 1") + print_msg("Building stage 1") + # change_dir(self.llvm_obj_dir_stage1) + # super(EB_LLVMcore, self).build_step(verbose, path) + change_dir(self.builddir) + shutil.rmtree('llvm.obj.1', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + if self.cfg['bootstrap']: + self.log.info("Building stage 2") + print_msg("Building stage 2") + # self.configure_step2() + # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + change_dir(self.builddir) + shutil.rmtree('llvm.obj.2', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + + self.log.info("Building stage 3") + print_msg("Building stage 3") + self.configure_step3() + self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) + # change_dir(self.builddir) + # shutil.rmtree('llvm.obj.3', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') + + # self.log.info("Building stage 4") + # print_msg("Building stage 4") + # self.configure_step4() + # self.build_with_prev_stage(self.llvm_obj_dir_stage3, self.llvm_obj_dir_stage4) + # # change_dir(self.builddir) + # # shutil.rmtree('llvm.obj.3', ignore_errors=True) + # # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') + + def test_step(self): + """Run Clang tests on final stage (unless disabled).""" + if not self.cfg['skip_all_tests']: + if self.cfg['bootstrap']: + basedir = self.llvm_obj_dir_stage3 + else: + basedir = self.llvm_obj_dir_stage1 + + change_dir(basedir) + orig_path = os.getenv('PATH') + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + lib_dir = os.path.join(basedir, 'lib') + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + lib_path = ':'.join([lib_dir, os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) + setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) + setvar('LD_LIBRARY_PATH', lib_path) + + cmd = "make %s check-all" % self.make_parallel_opts + (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + + setvar('PATH', orig_path) + setvar('LD_LIBRARY_PATH', orig_ld_library_path) + + rgx = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx.search(out) + if mch is None: + raise EasyBuildError("Failed to extract number of failed tests from output: %s", out) + num_failed = int(mch.group(1)) + if num_failed > self.cfg['test_suite_max_failed']: + raise EasyBuildError("Too many failed tests: %s", num_failed) + + + def install_step(self): + """Install stage 1 or 3 (if bootsrap) binaries.""" + if self.cfg['bootstrap']: + change_dir(self.llvm_obj_dir_stage3) + else: + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVMcore, self).install_step() + + def get_runtime_lib_path(self, base_dir, fail_ok=True): + """Return the path to the runtime libraries.""" + arch = get_cpu_architecture() + glob_pattern = os.path.join(base_dir, 'lib', '%s-*' % arch) + matches = glob.glob(glob_pattern) + if matches: + directory = os.path.basename(matches[0]) + res = os.path.join("lib", directory) + else: + if not fail_ok: + raise EasyBuildError("Could not find runtime library directory") + print_warning("Could not find runtime library directory") + res = "lib" + + return res + + def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): + self.runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) + shlib_ext = get_shared_lib_ext() + + if self.cfg['build_shared_libs']: + if custom_paths is None: + custom_paths = {} + ptr = custom_paths.setdefault('files', []) + for lib in ['LLVM', 'MLIR', 'clang', 'clang-cpp', 'lldb']: + ptr.append(os.path.join('lib', 'lib%s.%s' % (lib, shlib_ext))) + + return super().sanity_check_step(custom_paths=None, custom_commands=None, extension=False, extra_modules=None) + + def make_module_extra(self): + """Custom variables for Clang module.""" + txt = super(EB_LLVMcore, self).make_module_extra() + # we set the symbolizer path so that asan/tsan give meanfull output by default + asan_symbolizer_path = os.path.join(self.installdir, 'bin', 'llvm-symbolizer') + txt += self.module_generator.set_environment('ASAN_SYMBOLIZER_PATH', asan_symbolizer_path) + if self.cfg['python_bindings']: + txt += self.module_generator.prepend_paths('PYTHONPATH', os.path.join("lib", "python")) + return txt + + def make_module_req_guess(self): + """ + Clang can find its own headers and libraries but the .so's need to be in LD_LIBRARY_PATH + """ + guesses = super(EB_LLVMcore, self).make_module_req_guess() + guesses.update({ + 'CPATH': [], + 'LIBRARY_PATH': ['lib', 'lib64', self.runtime_lib_path], + 'LD_LIBRARY_PATH': ['lib', 'lib64', self.runtime_lib_path], + }) + return guesses From 28b5203318c3ddadacdbbc013cff8a060ca9acc9 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 13 Jun 2024 10:48:30 +0200 Subject: [PATCH 002/103] Make sure libgcc_s.so is not used --- easybuild/easyblocks/l/llvmcore.py | 64 ++++++++++++++++++------------ 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 09312cbf420..035f3e8e259 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -49,7 +49,7 @@ from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP from easybuild.easyblocks.generic.cmakemake import CMakeMake -cmake_opt_post3 = { +cmake_opt_post2 = { 'LIBCXX_CXX_ABI': 'libcxxabi', 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', @@ -128,6 +128,7 @@ def _general_configure_step(self): if self.cfg["enable_rtti"]: self._cmakeopts['LLVM_REQUIRES_RTTI'] = 'ON' self._cmakeopts['LLVM_ENABLE_RTTI'] = 'ON' + # Does not work with Flang # self._cmakeopts['LLVM_ENABLE_EH'] = 'ON' def configure_step(self): @@ -224,6 +225,7 @@ def configure_step2(self): self._general_configure_step() self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' + self._cmakeopts.update(cmake_opt_post2) # def configure_step3(self): # """Configure the second stage of the bootstrap.""" @@ -239,7 +241,7 @@ def configure_step3(self): self._general_configure_step() self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;lldb;mlir;polly;clang;flang"' self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' - self._cmakeopts.update(cmake_opt_post3) + self._cmakeopts.update(cmake_opt_post2) def build_with_prev_stage(self, prev_dir, stage_dir): """Build LLVM using the previous stage.""" @@ -250,7 +252,8 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self._cmakeopts['CMAKE_C_COMPILER'] = os.path.join(prev_dir, 'bin/clang') self._cmakeopts['CMAKE_CXX_COMPILER'] = os.path.join(prev_dir, 'bin/clang++') - # self._cmakeopts['CMAKE_ASM_COMPILER'] = os.path.join(prev_dir, 'bin/clang') + self._cmakeopts['CMAKE_ASM_COMPILER'] = os.path.join(prev_dir, 'bin/clang') + self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' self.add_cmake_opts() @@ -268,10 +271,13 @@ def build_with_prev_stage(self, prev_dir, stage_dir): os.path.join(prev_dir, lib_dir_runtime), ]) - # setvar('PATH', bin_dir + ":" + orig_path) - # setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) - # setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) + # Needed for passing the variables to the build command + setvar('PATH', bin_dir + ":" + orig_path) + setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) + setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) + # Needed for passing the variables to the configure command (the environment of configure step is isolated + # so the previous `setvar` does not affect it) self.cfg.update('preconfigopts', ' '.join([ 'PATH=%s:%s' % (bin_dir, orig_path), 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), @@ -291,30 +297,30 @@ def build_with_prev_stage(self, prev_dir, stage_dir): run_cmd(cmd, log_all=True) change_dir(curdir) - # setvar('PATH', orig_path) - # setvar('LIBRARY_PATH', orig_library_path) - # setvar('LD_LIBRARY_PATH', orig_ld_library_path) + setvar('PATH', orig_path) + setvar('LIBRARY_PATH', orig_library_path) + setvar('LD_LIBRARY_PATH', orig_ld_library_path) def build_step(self, verbose=False, path=None): """Build LLVM, and optionally build it using itself.""" self.log.info("Building stage 1") - print_msg("Building stage 1") - # change_dir(self.llvm_obj_dir_stage1) - # super(EB_LLVMcore, self).build_step(verbose, path) - change_dir(self.builddir) - shutil.rmtree('llvm.obj.1', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + print_msg("Building stage 1/3") + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVMcore, self).build_step(verbose, path) + # change_dir(self.builddir) + # shutil.rmtree('llvm.obj.1', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") - print_msg("Building stage 2") - # self.configure_step2() - # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - change_dir(self.builddir) - shutil.rmtree('llvm.obj.2', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + print_msg("Building stage 2/3") + self.configure_step2() + self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + # change_dir(self.builddir) + # shutil.rmtree('llvm.obj.2', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") - print_msg("Building stage 3") + print_msg("Building stage 3/3") self.configure_step3() self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) # change_dir(self.builddir) @@ -348,15 +354,21 @@ def test_step(self): cmd = "make %s check-all" % self.make_parallel_opts (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + self.log.debug(out) setvar('PATH', orig_path) setvar('LD_LIBRARY_PATH', orig_ld_library_path) - rgx = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) - mch = rgx.search(out) + rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_failed.search(out) if mch is None: - raise EasyBuildError("Failed to extract number of failed tests from output: %s", out) - num_failed = int(mch.group(1)) + rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_passed.search(out) + if mch is None: + raise EasyBuildError("Failed to extract test results from output") + num_failed = 0 + else: + num_failed = int(mch.group(1)) if num_failed > self.cfg['test_suite_max_failed']: raise EasyBuildError("Too many failed tests: %s", num_failed) From 557e729e7140f8e1bdc2d23141263302573378d8 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 14 Jun 2024 11:40:37 +0200 Subject: [PATCH 003/103] Modularized + switching some projects and full LLVM as optinal feature --- easybuild/easyblocks/l/llvmcore.py | 157 +++++++++++++++++++---------- 1 file changed, 103 insertions(+), 54 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 035f3e8e259..2ddfc19341f 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -49,7 +49,7 @@ from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP from easybuild.easyblocks.generic.cmakemake import CMakeMake -cmake_opt_post2 = { +remove_gcc_opts = { 'LIBCXX_CXX_ABI': 'libcxxabi', 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', @@ -58,8 +58,6 @@ 'LIBUNWIND_USE_COMPILER_RT': 'On', 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', 'CLANG_DEFAULT_RTLIB': 'compiler-rt', - 'CLANG_DEFAULT_LINKER': 'lld', - 'LLVM_POLLY_LINK_INTO_TOOLS': 'ON', } class EB_LLVMcore(CMakeMake): @@ -74,11 +72,16 @@ def extra_options(): 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + ', '.join(CLANG_TARGETS), CUSTOM], - 'bootstrap': [True, "Build LLVM using itself", CUSTOM], + 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], + 'full_llvm': [True, "Build LLVM without any dependency", CUSTOM], 'enable_rtti': [True, "Enable RTTI", CUSTOM], 'skip_all_tests': [False, "Skip running of tests", CUSTOM], 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], 'python_bindings': [False, "Install python bindings", CUSTOM], + 'build_bolt': [False, "Build the LLVM bolt binary optimizer", CUSTOM], + 'build_lld': [False, "Build the LLVM lld linker", CUSTOM], + 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], + 'usepolly': [False, "Build Clang with polly", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], }) @@ -93,10 +96,20 @@ def __init__(self, *args, **kwargs): self.llvm_obj_dir_stage3 = None # self.llvm_obj_dir_stage4 = None self.make_parallel_opts = "" + self.intermediate_projects = ['llvm', 'clang'] + self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] + self.final_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] if LooseVersion(self.version) < LooseVersion('18.1.6'): raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) + if self.cfg['full_llvm']: + if not self.cfg['bootstrap']: + raise EasyBuildError("Full LLVM build is only supported for bootstrap builds") + if not self.cfg['build_lld']: + raise EasyBuildError("Full LLVM build requires building lld") + self.build_shared = self.cfg.get('build_shared_libs', False) # self.cfg['start_dir'] = 'llvm' if self.build_shared: @@ -104,13 +117,10 @@ def __init__(self, *args, **kwargs): # self._projects = ['llvm'] # self._runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] - self._cmakeopts = { - 'LLVM_ENABLE_PROJECTS': 'llvm;lld;lldb;polly;mlir', - 'LLVM_ENABLE_RUNTIMES': 'compiler-rt;libunwind;libcxx;libcxxabi', - } + self._cmakeopts = {} self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) - def _general_configure_step(self): + def _configure_general_build(self): """General configuration step for LLVM.""" self._cmakeopts['CMAKE_BUILD_TYPE'] = self.build_type # If EB is launched from a venv, avoid giving priority to the venv's python @@ -119,18 +129,46 @@ def _general_configure_step(self): self._cmakeopts['LLVM_INCLUDE_BENCHMARKS'] = 'OFF' self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' + # Required for building the standalone libatomic (not depending on GCCcore) + # if self.cfg['full_llvm']: + # self._cmakeopts['COMPILER_RT_BUILD_STANDALONE_LIBATOMIC'] = 'ON' + if self.build_shared: self.cfg.update('configopts', '-DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON') if get_software_root('zlib'): self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' + z3_root = get_software_root("Z3") + if z3_root: + self.cfg.update('configopts', "-DLLVM_ENABLE_Z3_SOLVER=ON") + self.cfg.update('configopts', "-DLLVM_Z3_INSTALL_DIR=%s" % z3_root) + if self.cfg["enable_rtti"]: self._cmakeopts['LLVM_REQUIRES_RTTI'] = 'ON' self._cmakeopts['LLVM_ENABLE_RTTI'] = 'ON' # Does not work with Flang # self._cmakeopts['LLVM_ENABLE_EH'] = 'ON' + def _configure_intermediate_build(self): + """Configure the intermediate stages of the build.""" + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.intermediate_runtimes) + if self.cfg['build_lld']: + self._cmakeopts['CLANG_DEFAULT_LINKER'] = 'lld' + + def _configure_final_build(self): + """Configure the final stage of the build.""" + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.final_projects) + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.final_runtimes) + if self.cfg['build_lld']: + self._cmakeopts['CLANG_DEFAULT_LINKER'] = 'lld' + + # Make sure tests are not running with more than `--parallel` tasks + self._cmakeopts['LLVM_LIT_ARGS'] = '-j %s' % self.cfg['parallel'] + if self.cfg['usepolly']: + self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' + def configure_step(self): """ Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target @@ -147,17 +185,29 @@ def configure_step(self): self.log.info("Initialising for bootstrap build.") self.llvm_obj_dir_stage2 = os.path.join(self.builddir, 'llvm.obj.2') self.llvm_obj_dir_stage3 = os.path.join(self.builddir, 'llvm.obj.3') - # self.llvm_obj_dir_stage4 = os.path.join(self.builddir, 'llvm.obj.4') + self.final_dir = self.llvm_obj_dir_stage3 mkdir(self.llvm_obj_dir_stage2) mkdir(self.llvm_obj_dir_stage3) - # mkdir(self.llvm_obj_dir_stage4) - self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' - self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' + self._configure_intermediate_build() + else: + self.log.info("Initialising for single stage build.") + self._configure_final_build() + self.final_dir = self.llvm_obj_dir_stage1 if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: self.log.debug("Disabling the sanitizer tests") self.disable_sanitizer_tests() + if self.cfg['usepolly']: + self.final_projects.append('polly') + if self.cfg['build_lld']: + self.intermediate_projects.append('lld') + self.final_projects.append('lld') + if self.cfg['build_lldb']: + self.final_projects.append('lldb') + if self.cfg['build_bolt']: + self.final_projects.append('bolt') + gcc_prefix = get_software_root('GCCcore') # If that doesn't work, try with GCC if gcc_prefix is None: @@ -168,7 +218,7 @@ def configure_step(self): self._cmakeopts['GCC_INSTALL_PREFIX'] = gcc_prefix self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) - self._general_configure_step() + self._configure_general_build() build_targets = self.cfg['build_targets'] if build_targets is None: @@ -214,34 +264,26 @@ def add_cmake_opts(self): base_opts.append('-D%s=%s' % (k, v)) self.cfg['configopts'] = ' '.join(base_opts) - self.log.debug("-%"*50) - self.log.debug("Using %s as configopts", self._cfgopts) - self.log.debug("Using %s as cmakeopts", self._cmakeopts) - self.log.debug("-%"*50) + # self.log.debug("-%"*50) + # self.log.debug("Using %s as configopts", self._cfgopts) + # self.log.debug("Using %s as cmakeopts", self._cmakeopts) + # self.log.debug("-%"*50) def configure_step2(self): """Configure the second stage of the bootstrap.""" self._cmakeopts = {} - self._general_configure_step() - self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' - self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' - self._cmakeopts.update(cmake_opt_post2) - - # def configure_step3(self): - # """Configure the second stage of the bootstrap.""" - # self._cmakeopts = {} - # self._general_configure_step() - # self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;clang"' - # self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' - # self._cmakeopts.update(cmake_opt_post3) + self._configure_general_build() + self._configure_intermediate_build() + if self.cfg['full_llvm']: + self._cmakeopts.update(remove_gcc_opts) def configure_step3(self): """Configure the third stage of the bootstrap.""" self._cmakeopts = {} - self._general_configure_step() - self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"llvm;lld;lldb;mlir;polly;clang;flang"' - self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"compiler-rt;libunwind;libcxx;libcxxabi"' - self._cmakeopts.update(cmake_opt_post2) + self._configure_general_build() + self._configure_final_build() + if self.cfg['full_llvm']: + self._cmakeopts.update(remove_gcc_opts) def build_with_prev_stage(self, prev_dir, stage_dir): """Build LLVM using the previous stage.""" @@ -278,6 +320,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # Needed for passing the variables to the configure command (the environment of configure step is isolated # so the previous `setvar` does not affect it) + _preconfigopts = self.cfg.get('preconfigopts', '') self.cfg.update('preconfigopts', ' '.join([ 'PATH=%s:%s' % (bin_dir, orig_path), 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), @@ -287,6 +330,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): builddir=stage_dir, srcdir=os.path.join(self.llvm_src_dir, "llvm") ) + self.cfg.update('preconfigopts', _preconfigopts) # change_dir(stage_dir) # self.log.debug("Configuring %s", stage_dir) @@ -303,8 +347,9 @@ def build_with_prev_stage(self, prev_dir, stage_dir): def build_step(self, verbose=False, path=None): """Build LLVM, and optionally build it using itself.""" - self.log.info("Building stage 1") - print_msg("Building stage 1/3") + if self.cfg['bootstrap']: + self.log.info("Building stage 1") + print_msg("Building stage 1/3") change_dir(self.llvm_obj_dir_stage1) super(EB_LLVMcore, self).build_step(verbose, path) # change_dir(self.builddir) @@ -327,21 +372,10 @@ def build_step(self, verbose=False, path=None): # shutil.rmtree('llvm.obj.3', ignore_errors=True) # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') - # self.log.info("Building stage 4") - # print_msg("Building stage 4") - # self.configure_step4() - # self.build_with_prev_stage(self.llvm_obj_dir_stage3, self.llvm_obj_dir_stage4) - # # change_dir(self.builddir) - # # shutil.rmtree('llvm.obj.3', ignore_errors=True) - # # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') - def test_step(self): """Run Clang tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: - if self.cfg['bootstrap']: - basedir = self.llvm_obj_dir_stage3 - else: - basedir = self.llvm_obj_dir_stage1 + basedir = self.final_dir change_dir(basedir) orig_path = os.getenv('PATH') @@ -352,7 +386,7 @@ def test_step(self): setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) setvar('LD_LIBRARY_PATH', lib_path) - cmd = "make %s check-all" % self.make_parallel_opts + cmd = "make -j 1 check-all" (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) self.log.debug(out) @@ -372,15 +406,30 @@ def test_step(self): if num_failed > self.cfg['test_suite_max_failed']: raise EasyBuildError("Too many failed tests: %s", num_failed) - def install_step(self): """Install stage 1 or 3 (if bootsrap) binaries.""" - if self.cfg['bootstrap']: - change_dir(self.llvm_obj_dir_stage3) - else: - change_dir(self.llvm_obj_dir_stage1) + basedir = self.final_dir + change_dir(basedir) + + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + + # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols + # e.g. when calling the compiled clang-ast-dump for stage 3 + lib_path = ':'.join([ + basedir, + os.path.join(basedir, lib_dir_runtime), + ]) + + _preinstallopts = self.cfg.get('preinstallopts', '') + self.cfg.update('preinstallopts', ' '.join([ + 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) + ])) + super(EB_LLVMcore, self).install_step() + self.cfg.update('preinstallopts', _preinstallopts) + def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" arch = get_cpu_architecture() From 3aab618349227d7bad42c6952a8faeb71e08ee99 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 18 Jun 2024 18:11:09 +0200 Subject: [PATCH 004/103] Working without `clang-tools-extra` and `openmp` --- easybuild/easyblocks/l/llvmcore.py | 385 ++++++++++++++++++++--------- 1 file changed, 274 insertions(+), 111 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 2ddfc19341f..0e60df52574 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -40,24 +40,121 @@ from easybuild.tools.config import build_option from easybuild.tools.environment import setvar from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, - mkdir) + mkdir, symlink, which) from easybuild.tools.modules import get_software_root, get_software_version from easybuild.tools.run import run_cmd from easybuild.tools.systemtools import (get_cpu_architecture, get_shared_lib_ext) +from easybuild.tools.toolchain.toolchain import Toolchain from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP from easybuild.easyblocks.generic.cmakemake import CMakeMake +# remove_gcc_opts = { +# 'LIBCXX_CXX_ABI': 'libcxxabi', +# 'LIBCXX_USE_COMPILER_RT': 'On', +# 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', +# 'LIBCXXABI_USE_COMPILER_RT': 'On', +# 'LIBCXX_HAS_GCC_S_LIB': 'Off', +# 'LIBUNWIND_USE_COMPILER_RT': 'On', +# 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', +# 'CLANG_DEFAULT_RTLIB': 'compiler-rt', +# } + +# cmake +# ‘-DCMAKE_PREFIX_PATH=/home/ampandey/rocm-toolchain/rocm-5.6/llvm;/home/ampandey/rocm-toolchain/rocm-5.6’ +# -DCMAKE_BUILD_TYPE=Release +# -DCMAKE_VERBOSE_MAKEFILE=1 +# -DCPACK_GENERATOR=DEB +# ‘-DCMAKE_INSTALL_RPATH=$ORIGIN:$ORIGIN/…/lib:$ORIGIN/…/lib64:/opt/rocm-5.6.0-9999/lib:/opt/rocm-5.6.0-9999/lib64:/opt/rocm/lib:/opt/rocm/lib64:$ORIGIN/…/llvm/lib’ +# -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE +# -DROCM_PATCH_VERSION=50600 +# -DCMAKE_INSTALL_PREFIX=/home/ampandey/rocm-toolchain/rocm-5.6 +# -DCPACK_PACKAGING_INSTALL_PREFIX=/home/ampandey/rocm-toolchain/rocm-5.6 +# -DCMAKE_INSTALL_PREFIX=/home/ampandey/rocm-toolchain/rocm-5.6/llvm +# ‘-DLLVM_TARGETS_TO_BUILD=AMDGPU;X86’ +# ‘-DLLVM_ENABLE_PROJECTS=clang;lld;clang-tools-extra’ +# ‘-DLLVM_ENABLE_RUNTIMES=libcxx;libcxxabi;compiler-rt’ +# -DLIBCXX_ENABLE_SHARED=OFF +# -DLIBCXX_ENABLE_STATIC=ON +# -DLIBCXX_INSTALL_LIBRARY=OFF +# -DLIBCXX_INSTALL_HEADERS=OFF +# -DLIBCXXABI_ENABLE_SHARED=OFF +# -DLIBCXXABI_ENABLE_STATIC=ON +# -DLIBCXXABI_INSTALL_STATIC_LIBRARY=OFF +# -DLLVM_BUILD_DOCS=OFF +# -DLLVM_ENABLE_SPHINX=OFF +# -DSPHINX_WARNINGS_AS_ERRORS=OFF +# -DSPHINX_OUTPUT_MAN=OFF +# -DLLVM_ENABLE_ASSERTIONS=1 +# -DLLVM_ENABLE_Z3_SOLVER=OFF +# -DLLVM_ENABLE_ZLIB=ON +# -DLLVM_AMDGPU_ALLOW_NPI_TARGETS=ON +# -DCLANG_DEFAULT_PIE_ON_LINUX=0 +# -DCLANG_DEFAULT_LINKER=lld +# -DCLANG_DEFAULT_RTLIB=compiler-rt +# -DCLANG_DEFAULT_UNWINDLIB=libgcc +# -DPACKAGE_VENDOR=AMD +# -DLLVM_BUILD_LLVM_DYLIB=OFF +# -DLLVM_LINK_LLVM_DYLIB=OFF +# -DLLVM_ENABLE_LIBCXX=OFF +# /home/ampandey/rocm-toolchain/build/…/external/llvm-project/llvm + remove_gcc_opts = { - 'LIBCXX_CXX_ABI': 'libcxxabi', + # 'LLVM_LIBC_FULL_BUILD': 'On', + # 'LLVM_LIBC_INCLUDE_SCUDO': 'On', + 'LIBCXX_USE_COMPILER_RT': 'On', + 'LIBCXX_CXX_ABI': 'libcxxabi', + 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', 'LIBCXXABI_USE_COMPILER_RT': 'On', - 'LIBCXX_HAS_GCC_S_LIB': 'Off', + 'LIBUNWIND_USE_COMPILER_RT': 'On', + + 'SANITIZER_USE_STATIC_LLVM_UNWINDER': 'On', + # 'SANITIZER_USE_STATIC_CXX_ABI': 'On', + 'COMPILER_RT_USE_LIBCXX': 'On', + 'COMPILER_RT_USE_LLVM_UNWINDER': 'On', + # 'COMPILER_RT_CXX_LIBRARY': 'libcxx', + 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', + 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html + # 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', + 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', + # 'COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC': 'On', + # 'COMPILER_RT_SCUDO_STANDALONE_BUILD_SHARED': 'Off', + # Required for building the standalone libatomic (not depending on GCCcore) + # 'COMPILER_RT_BUILD_STANDALONE_LIBATOMIC': 'On', + 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', 'CLANG_DEFAULT_RTLIB': 'compiler-rt', + 'CLANG_DEFAULT_LINKER': 'lld', + 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', + + 'LIBCXX_HAS_GCC_S_LIB': 'Off', + 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', + 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', + # 'COMPILER_RT_HAS_GCC_S_LIB': 'Off', + # 'CLANG_HAS_GCC_S_LIB': 'Off', +} + +disable_werror = { + 'LLVM_ENABLE_WERROR': 'Off', + 'BENCHMARK_ENABLE_WERROR': 'Off', + 'COMPILER_RT_ENABLE_WERROR': 'Off', + 'LIBC_WNO_ERROR': 'On', + 'LIBCXX_ENABLE_WERROR': 'Off', + 'LIBUNWIND_ENABLE_WERROR': 'Off', + 'FLANG_ENABLE_WERROR': 'Off', +} + +general_opts = { + # If EB is launched from a venv, avoid giving priority to the venv's python + 'Python3_FIND_VIRTUALENV': 'STANDARD', + 'LLVM_INSTALL_UTILS': 'ON', + 'LLVM_INCLUDE_BENCHMARKS': 'OFF', + 'CMAKE_VERBOSE_MAKEFILE': 'ON', + # 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html } class EB_LLVMcore(CMakeMake): @@ -78,10 +175,14 @@ def extra_options(): 'skip_all_tests': [False, "Skip running of tests", CUSTOM], 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], 'python_bindings': [False, "Install python bindings", CUSTOM], + 'build_clang_extras': [False, "Build the LLVM Clang extra tools", CUSTOM], 'build_bolt': [False, "Build the LLVM bolt binary optimizer", CUSTOM], 'build_lld': [False, "Build the LLVM lld linker", CUSTOM], 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], + 'build_runtimes': [True, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], + 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], + 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], }) @@ -95,77 +196,120 @@ def __init__(self, *args, **kwargs): self.llvm_obj_dir_stage2 = None self.llvm_obj_dir_stage3 = None # self.llvm_obj_dir_stage4 = None - self.make_parallel_opts = "" self.intermediate_projects = ['llvm', 'clang'] self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] - self.final_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + self.final_runtimes = [] + if self.cfg['build_runtimes']: + self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + if self.cfg['build_openmp']: + self.final_runtimes.append('openmp') if LooseVersion(self.version) < LooseVersion('18.1.6'): raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) + # Shared + self.build_shared = self.cfg.get('build_shared_libs', False) + if self.build_shared: + self.cfg['build_shared_libs'] = None + general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'ON' + general_opts['LLVM_LINK_LLVM_DYLIB'] = 'ON' + general_opts['LIBCXX_ENABLE_SHARED'] = 'ON' + general_opts['LIBCXXABI_ENABLE_SHARED'] = 'ON' + general_opts['LIBUNWIND_ENABLE_SHARED'] = 'ON' + else: + general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'OFF' + general_opts['LLVM_LINK_LLVM_DYLIB'] = 'OFF' + general_opts['LIBCXX_ENABLE_SHARED'] = 'OFF' + general_opts['LIBCXXABI_ENABLE_SHARED'] = 'OFF' + general_opts['LIBUNWIND_ENABLE_SHARED'] = 'OFF' + general_opts['LIBCXX_ENABLE_STATIC'] = 'ON' + general_opts['LIBCXXABI_ENABLE_STATIC'] = 'ON' + + # RTTI + if self.cfg["enable_rtti"]: + general_opts['LLVM_REQUIRES_RTTI'] = 'ON' + general_opts['LLVM_ENABLE_RTTI'] = 'ON' + # Does not work with Flang + # general_opts['LLVM_ENABLE_EH'] = 'ON' + + # Other vustom options if self.cfg['full_llvm']: if not self.cfg['bootstrap']: - raise EasyBuildError("Full LLVM build is only supported for bootstrap builds") + raise EasyBuildError("Full LLVM build irequires bootstrap build") if not self.cfg['build_lld']: raise EasyBuildError("Full LLVM build requires building lld") + if not self.cfg['build_runtimes']: + raise EasyBuildError("Full LLVM build requires building runtimes") + if self.cfg['disable_werror']: + general_opts.update(disable_werror) + if self.cfg['usepolly']: + self.final_projects.append('polly') + if self.cfg['build_clang_extras']: + self.final_projects.append('clang-tools-extra') + if self.cfg['build_lld']: + self.intermediate_projects.append('lld') + self.final_projects.append('lld') + if self.cfg['build_lldb']: + self.final_projects.append('lldb') + if self.cfg['build_bolt']: + self.final_projects.append('bolt') - self.build_shared = self.cfg.get('build_shared_libs', False) - # self.cfg['start_dir'] = 'llvm' - if self.build_shared: - self.cfg['build_shared_libs'] = None + # Build targets + build_targets = self.cfg['build_targets'] + if build_targets is None: + arch = get_cpu_architecture() + try: + default_targets = DEFAULT_TARGETS_MAP[arch][:] + self.cfg['build_targets'] = build_targets = default_targets + self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) + except KeyError: + raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) + + unknown_targets = [target for target in build_targets if target not in CLANG_TARGETS] + + if unknown_targets: + raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", + ', '.join(unknown_targets), ', '.join(CLANG_TARGETS)) + + general_opts['CMAKE_BUILD_TYPE'] = self.build_type + general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir + if self.toolchain.options['pic']: + general_opts['CMAKE_POSITION_INDEPENDENT_CODE'] = 'ON' + + general_opts['LLVM_TARGETS_TO_BUILD'] = ';'.join(build_targets) - # self._projects = ['llvm'] - # self._runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] self._cmakeopts = {} + self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) def _configure_general_build(self): """General configuration step for LLVM.""" - self._cmakeopts['CMAKE_BUILD_TYPE'] = self.build_type - # If EB is launched from a venv, avoid giving priority to the venv's python - self._cmakeopts['Python3_FIND_VIRTUALENV'] = 'STANDARD' - self._cmakeopts['LLVM_INSTALL_UTILS'] = 'ON' - self._cmakeopts['LLVM_INCLUDE_BENCHMARKS'] = 'OFF' self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' - # Required for building the standalone libatomic (not depending on GCCcore) - # if self.cfg['full_llvm']: - # self._cmakeopts['COMPILER_RT_BUILD_STANDALONE_LIBATOMIC'] = 'ON' - - if self.build_shared: - self.cfg.update('configopts', '-DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON') - if get_software_root('zlib'): self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' z3_root = get_software_root("Z3") if z3_root: - self.cfg.update('configopts', "-DLLVM_ENABLE_Z3_SOLVER=ON") - self.cfg.update('configopts', "-DLLVM_Z3_INSTALL_DIR=%s" % z3_root) + self._cmakeopts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' + self._cmakeopts['LLVM_Z3_INSTALL_DIR'] = z3_root + + self._cmakeopts.update(general_opts) - if self.cfg["enable_rtti"]: - self._cmakeopts['LLVM_REQUIRES_RTTI'] = 'ON' - self._cmakeopts['LLVM_ENABLE_RTTI'] = 'ON' - # Does not work with Flang - # self._cmakeopts['LLVM_ENABLE_EH'] = 'ON' def _configure_intermediate_build(self): """Configure the intermediate stages of the build.""" self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.intermediate_runtimes) - if self.cfg['build_lld']: - self._cmakeopts['CLANG_DEFAULT_LINKER'] = 'lld' def _configure_final_build(self): """Configure the final stage of the build.""" self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.final_projects) self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.final_runtimes) - if self.cfg['build_lld']: - self._cmakeopts['CLANG_DEFAULT_LINKER'] = 'lld' # Make sure tests are not running with more than `--parallel` tasks - self._cmakeopts['LLVM_LIT_ARGS'] = '-j %s' % self.cfg['parallel'] + self._cmakeopts['LLVM_LIT_ARGS'] = '"-j %s"' % self.cfg['parallel'] if self.cfg['usepolly']: self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' @@ -173,13 +317,12 @@ def configure_step(self): """ Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target """ - gcc_version = get_software_version('GCCcore') - if LooseVersion(gcc_version) < LooseVersion('13'): - raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) - + # Parallel build + self.make_parallel_opts = "" if self.cfg['parallel']: self.make_parallel_opts = "-j %s" % self.cfg['parallel'] + # Bootstrap self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Initialising for bootstrap build.") @@ -188,9 +331,23 @@ def configure_step(self): self.final_dir = self.llvm_obj_dir_stage3 mkdir(self.llvm_obj_dir_stage2) mkdir(self.llvm_obj_dir_stage3) - self._configure_intermediate_build() else: self.log.info("Initialising for single stage build.") + self.final_dir = self.llvm_obj_dir_stage1 + + # return + + gcc_version = get_software_version('GCCcore') + if LooseVersion(gcc_version) < LooseVersion('13'): + raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) + + self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') + if self.cfg['bootstrap']: + self._configure_intermediate_build() + # if self.cfg['full_llvm']: + # self.intermediate_projects.append('libc') + # self.final_projects.append('libc') + else: self._configure_final_build() self.final_dir = self.llvm_obj_dir_stage1 @@ -198,16 +355,6 @@ def configure_step(self): self.log.debug("Disabling the sanitizer tests") self.disable_sanitizer_tests() - if self.cfg['usepolly']: - self.final_projects.append('polly') - if self.cfg['build_lld']: - self.intermediate_projects.append('lld') - self.final_projects.append('lld') - if self.cfg['build_lldb']: - self.final_projects.append('lldb') - if self.cfg['build_bolt']: - self.final_projects.append('bolt') - gcc_prefix = get_software_root('GCCcore') # If that doesn't work, try with GCC if gcc_prefix is None: @@ -220,26 +367,6 @@ def configure_step(self): self._configure_general_build() - build_targets = self.cfg['build_targets'] - if build_targets is None: - arch = get_cpu_architecture() - try: - default_targets = DEFAULT_TARGETS_MAP[arch][:] - self.cfg['build_targets'] = build_targets = default_targets - self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) - except KeyError: - raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) - - unknown_targets = [target for target in build_targets if target not in CLANG_TARGETS] - - if unknown_targets: - raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", - ', '.join(unknown_targets), ', '.join(CLANG_TARGETS)) - - self.cfg.update('configopts', '-DLLVM_TARGETS_TO_BUILD="%s"' % ';'.join(build_targets)) - - self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) - self.add_cmake_opts() super(EB_LLVMcore, self).configure_step( builddir=self.llvm_obj_dir_stage1, @@ -264,10 +391,10 @@ def add_cmake_opts(self): base_opts.append('-D%s=%s' % (k, v)) self.cfg['configopts'] = ' '.join(base_opts) - # self.log.debug("-%"*50) - # self.log.debug("Using %s as configopts", self._cfgopts) - # self.log.debug("Using %s as cmakeopts", self._cmakeopts) - # self.log.debug("-%"*50) + self.log.debug("-%"*50) + self.log.debug("Using %s as configopts", self._cfgopts) + self.log.debug("Using %s as cmakeopts", self._cmakeopts) + self.log.debug("-%"*50) def configure_step2(self): """Configure the second stage of the bootstrap.""" @@ -289,7 +416,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): """Build LLVM using the previous stage.""" curdir = os.getcwd() orig_path = os.getenv('PATH') - orig_library_path = os.getenv('LIBRARY_PATH') + # orig_library_path = os.getenv('LIBRARY_PATH') orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') self._cmakeopts['CMAKE_C_COMPILER'] = os.path.join(prev_dir, 'bin/clang') @@ -301,36 +428,69 @@ def build_with_prev_stage(self, prev_dir, stage_dir): bin_dir = os.path.join(prev_dir, 'bin') prev_lib_dir = os.path.join(prev_dir, 'lib') - curr_lib_dir = os.path.join(stage_dir, 'lib') + # curr_lib_dir = os.path.join(stage_dir, 'lib') lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols # e.g. when calling the compiled clang-ast-dump for stage 3 lib_path = ':'.join([ - curr_lib_dir, - os.path.join(curr_lib_dir, lib_dir_runtime), - prev_lib_dir, + # curr_lib_dir, + # os.path.join(curr_lib_dir, lib_dir_runtime), + # prev_lib_dir, os.path.join(prev_dir, lib_dir_runtime), ]) # Needed for passing the variables to the build command setvar('PATH', bin_dir + ":" + orig_path) - setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) + # setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) # Needed for passing the variables to the configure command (the environment of configure step is isolated # so the previous `setvar` does not affect it) - _preconfigopts = self.cfg.get('preconfigopts', '') - self.cfg.update('preconfigopts', ' '.join([ - 'PATH=%s:%s' % (bin_dir, orig_path), - 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), - 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) - ])) - super(EB_LLVMcore, self).configure_step( - builddir=stage_dir, - srcdir=os.path.join(self.llvm_src_dir, "llvm") - ) - self.cfg.update('preconfigopts', _preconfigopts) + # _preconfigopts = self.cfg.get('preconfigopts', '') + # self.cfg.update('preconfigopts', ' '.join([ + # 'PATH=%s:%s' % (bin_dir, orig_path), + # 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), + # 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) + # ])) + # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 + if build_option('rpath'): + my_toolchain = Toolchain(name='llvm', version='1') + my_toolchain.prepare_rpath_wrappers() + self.log.info("Prepared rpath wrappers") + + # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory + # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075 + clang_wrapper_dir = os.path.dirname(which('clang')) + symlink(os.path.join(prev_dir, 'opt'), os.path.join(clang_wrapper_dir, 'opt')) + + # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling + # Clang by default warns about that, and then some configure tests use -Werror which turns those warnings + # into errors. As a result, those configure tests fail, even though the compiler supports the requested + # functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles without + # resulting in relocation errors). + # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100 + # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether + cflags = os.getenv('CFLAGS') + cxxflags = os.getenv('CXXFLAGS') + setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) + setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) + + # prepend_config = ' '.join([ + # 'PATH=%s:%s' % (bin_dir, orig_path), + # # 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), + # # 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) + # ]) + + change_dir(stage_dir) + self.log.debug("Configuring %s", stage_dir) + cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) + run_cmd(cmd, log_all=True) + # super(EB_LLVMcore, self).configure_step( + # builddir=stage_dir, + # srcdir=os.path.join(self.llvm_src_dir, "llvm") + # ) + # self.cfg.update('preconfigopts', _preconfigopts) # change_dir(stage_dir) # self.log.debug("Configuring %s", stage_dir) @@ -342,7 +502,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): change_dir(curdir) setvar('PATH', orig_path) - setvar('LIBRARY_PATH', orig_library_path) + # setvar('LIBRARY_PATH', orig_library_path) setvar('LD_LIBRARY_PATH', orig_ld_library_path) def build_step(self, verbose=False, path=None): @@ -350,25 +510,28 @@ def build_step(self, verbose=False, path=None): if self.cfg['bootstrap']: self.log.info("Building stage 1") print_msg("Building stage 1/3") - change_dir(self.llvm_obj_dir_stage1) - super(EB_LLVMcore, self).build_step(verbose, path) - # change_dir(self.builddir) - # shutil.rmtree('llvm.obj.1', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + # change_dir(self.llvm_obj_dir_stage1) + # super(EB_LLVMcore, self).build_step(verbose, path) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.1', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - self.configure_step2() - self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - # change_dir(self.builddir) - # shutil.rmtree('llvm.obj.2', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + # self.configure_step2() + # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.2', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") self.configure_step3() self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") # shutil.rmtree('llvm.obj.3', ignore_errors=True) # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') @@ -379,19 +542,19 @@ def test_step(self): change_dir(basedir) orig_path = os.getenv('PATH') - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - lib_dir = os.path.join(basedir, 'lib') - lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - lib_path = ':'.join([lib_dir, os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) + # orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + # lib_dir = os.path.join(basedir, 'lib') + # lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + # lib_path = ':'.join([lib_dir, os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) - setvar('LD_LIBRARY_PATH', lib_path) + # setvar('LD_LIBRARY_PATH', lib_path) cmd = "make -j 1 check-all" (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) self.log.debug(out) setvar('PATH', orig_path) - setvar('LD_LIBRARY_PATH', orig_ld_library_path) + # setvar('LD_LIBRARY_PATH', orig_ld_library_path) rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) @@ -421,7 +584,7 @@ def install_step(self): os.path.join(basedir, lib_dir_runtime), ]) - _preinstallopts = self.cfg.get('preinstallopts', '') + _preinstallopts = self. cfg.get('preinstallopts', '') self.cfg.update('preinstallopts', ' '.join([ 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) ])) @@ -450,7 +613,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F self.runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) shlib_ext = get_shared_lib_ext() - if self.cfg['build_shared_libs']: + if self.build_shared: if custom_paths is None: custom_paths = {} ptr = custom_paths.setdefault('files', []) From 485b411a59fad495e0dba988e2ebff4489c1eef8 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 21 Jun 2024 11:51:28 +0200 Subject: [PATCH 005/103] Workin openmp installation --- easybuild/easyblocks/l/llvmcore.py | 82 ++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 0e60df52574..5b7900adbf5 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -61,6 +61,7 @@ # 'CLANG_DEFAULT_RTLIB': 'compiler-rt', # } +# https://discourse.llvm.org/t/should-buildbots-switch-to-enable-runtimes-instead-of-enable-projects-for-compiler-rt/65042/12 # cmake # ‘-DCMAKE_PREFIX_PATH=/home/ampandey/rocm-toolchain/rocm-5.6/llvm;/home/ampandey/rocm-toolchain/rocm-5.6’ # -DCMAKE_BUILD_TYPE=Release @@ -100,6 +101,46 @@ # -DLLVM_ENABLE_LIBCXX=OFF # /home/ampandey/rocm-toolchain/build/…/external/llvm-project/llvm + +# https://github.com/llvm/llvm-project/issues/72108 +# -DCMAKE_BUILD_TYPE="Release" \ +# -DCMAKE_C_FLAGS="-Wno-backend-plugin" \ +# -DCMAKE_CXX_FLAGS="-Wno-backend-plugin" \ +# -DCMAKE_C_COMPILER="clang" \ +# -DCMAKE_CXX_COMPILER="clang++" \ +# -DLLVM_ENABLE_CLASSIC_FLANG=ON \ +# -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;openmp" \ +# -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi" \ +# -DLLVM_LIBDIR_SUFFIX=64 \ +# -DLLVM_USE_LINKER=lld \ +# -DLLVM_PARALLEL_COMPILE_JOBS="96" \ +# -DLLVM_PARALLEL_LINK_JOBS=96 \ +# -DLLVM_ENABLE_NEW_PASS_MANAGER=ON \ +# -DLLVM_TARGETS_TO_BUILD="X86;BPF;WebAssembly" \ +# -DLLVM_ENABLE_LIBCXX=OFF \ +# -DLLVM_STATIC_LINK_CXX_STDLIB=OFF \ +# -DLLVM_BINUTILS_INCDIR="/usr/include" \ +# -DLLVM_ENABLE_TERMINFO=OFF \ +# -DLLVM_ENABLE_LIBXML2=OFF \ +# -DLLVM_ENABLE_LIBEDIT=OFF \ +# -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \ +# -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ +# -DCOMPILER_RT_BUILD_XRAY=OFF \ +# -DCOMPILER_RT_BUILD_ORC=OFF \ +# -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ +# -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=OFF \ +# -DOPENMP_ENABLE_LIBOMPTARGET=OFF \ +# -DOPENMP_ENABLE_OMPT_TOOLS=OFF \ +# -DOPENMP_ENABLE_TESTING=OFF \ +# -DCOMPILER_RT_CXX_LIBRARY=libcxx \ +# -DSANITIZER_CXX_ABI=libc++ \ +# -DSANITIZER_CXX_ABI_INTREE=ON \ +# -DSANITIZER_USE_STATIC_CXX_ABI=ON \ +# -DSANITIZER_TEST_CXX=libc++ \ +# -DSANITIZER_TEST_CXX_INTREE=ON \ +# -DSANITIZER_USE_STATIC_TEST_CXX=ON \ +# -DSANITIZER_LIT_USE_LIBCXX=ON \ + remove_gcc_opts = { # 'LLVM_LIBC_FULL_BUILD': 'On', # 'LLVM_LIBC_INCLUDE_SCUDO': 'On', @@ -119,7 +160,7 @@ # 'COMPILER_RT_CXX_LIBRARY': 'libcxx', 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html - # 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', + 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', # 'COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC': 'On', # 'COMPILER_RT_SCUDO_STANDALONE_BUILD_SHARED': 'Off', @@ -136,6 +177,8 @@ 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', # 'COMPILER_RT_HAS_GCC_S_LIB': 'Off', # 'CLANG_HAS_GCC_S_LIB': 'Off', + + 'OPENMP_ENABLE_OMPT_TOOLS': 'Off', } disable_werror = { @@ -145,6 +188,7 @@ 'LIBC_WNO_ERROR': 'On', 'LIBCXX_ENABLE_WERROR': 'Off', 'LIBUNWIND_ENABLE_WERROR': 'Off', + 'OPENMP_ENABLE_WERROR': 'Off', 'FLANG_ENABLE_WERROR': 'Off', } @@ -203,7 +247,7 @@ def __init__(self, *args, **kwargs): if self.cfg['build_runtimes']: self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] if self.cfg['build_openmp']: - self.final_runtimes.append('openmp') + self.final_projects.append('openmp') if LooseVersion(self.version) < LooseVersion('18.1.6'): raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) @@ -225,6 +269,7 @@ def __init__(self, *args, **kwargs): general_opts['LIBUNWIND_ENABLE_SHARED'] = 'OFF' general_opts['LIBCXX_ENABLE_STATIC'] = 'ON' general_opts['LIBCXXABI_ENABLE_STATIC'] = 'ON' + general_opts['LIBUNWIND_ENABLE_STATIC'] = 'ON' # RTTI if self.cfg["enable_rtti"]: @@ -427,7 +472,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self.add_cmake_opts() bin_dir = os.path.join(prev_dir, 'bin') - prev_lib_dir = os.path.join(prev_dir, 'lib') + # prev_lib_dir = os.path.join(prev_dir, 'lib') # curr_lib_dir = os.path.join(stage_dir, 'lib') lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) @@ -435,13 +480,16 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # e.g. when calling the compiled clang-ast-dump for stage 3 lib_path = ':'.join([ # curr_lib_dir, - # os.path.join(curr_lib_dir, lib_dir_runtime), + os.path.join(stage_dir, lib_dir_runtime), # prev_lib_dir, os.path.join(prev_dir, lib_dir_runtime), ]) # Needed for passing the variables to the build command setvar('PATH', bin_dir + ":" + orig_path) + # if cpath: + # orig_cpath = os.getenv('CPATH') + # setvar('CPATH', os.path.join(prev_dir, 'include', 'c++', 'v1') + ":" + orig_cpath) # setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) @@ -502,6 +550,8 @@ def build_with_prev_stage(self, prev_dir, stage_dir): change_dir(curdir) setvar('PATH', orig_path) + # if cpath: + # setvar('CPATH', orig_cpath) # setvar('LIBRARY_PATH', orig_library_path) setvar('LD_LIBRARY_PATH', orig_ld_library_path) @@ -510,21 +560,21 @@ def build_step(self, verbose=False, path=None): if self.cfg['bootstrap']: self.log.info("Building stage 1") print_msg("Building stage 1/3") - # change_dir(self.llvm_obj_dir_stage1) - # super(EB_LLVMcore, self).build_step(verbose, path) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.1', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVMcore, self).build_step(verbose, path) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.1', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - # self.configure_step2() - # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.2', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + self.configure_step2() + self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.2', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") From 4860cde800ae6e0fc52f8727e3d7a97ac210f836 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 21 Jun 2024 14:14:01 +0200 Subject: [PATCH 006/103] Working test-suite --- easybuild/easyblocks/l/llvmcore.py | 84 +++++++++--------------------- 1 file changed, 26 insertions(+), 58 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 5b7900adbf5..b6c832ba0d2 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -487,20 +487,8 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # Needed for passing the variables to the build command setvar('PATH', bin_dir + ":" + orig_path) - # if cpath: - # orig_cpath = os.getenv('CPATH') - # setvar('CPATH', os.path.join(prev_dir, 'include', 'c++', 'v1') + ":" + orig_cpath) - # setvar('LIBRARY_PATH', lib_path + ":" + orig_library_path) setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) - # Needed for passing the variables to the configure command (the environment of configure step is isolated - # so the previous `setvar` does not affect it) - # _preconfigopts = self.cfg.get('preconfigopts', '') - # self.cfg.update('preconfigopts', ' '.join([ - # 'PATH=%s:%s' % (bin_dir, orig_path), - # 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), - # 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) - # ])) # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 if build_option('rpath'): my_toolchain = Toolchain(name='llvm', version='1') @@ -524,35 +512,17 @@ def build_with_prev_stage(self, prev_dir, stage_dir): setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) - # prepend_config = ' '.join([ - # 'PATH=%s:%s' % (bin_dir, orig_path), - # # 'LIBRARY_PATH=%s:%s' % (lib_path, orig_library_path), - # # 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) - # ]) - change_dir(stage_dir) self.log.debug("Configuring %s", stage_dir) cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) run_cmd(cmd, log_all=True) - # super(EB_LLVMcore, self).configure_step( - # builddir=stage_dir, - # srcdir=os.path.join(self.llvm_src_dir, "llvm") - # ) - # self.cfg.update('preconfigopts', _preconfigopts) - - # change_dir(stage_dir) - # self.log.debug("Configuring %s", stage_dir) - # cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) - # run_cmd(cmd, log_all=True) + self.log.debug("Building %s", stage_dir) cmd = "make %s VERBOSE=1" % self.make_parallel_opts run_cmd(cmd, log_all=True) change_dir(curdir) setvar('PATH', orig_path) - # if cpath: - # setvar('CPATH', orig_cpath) - # setvar('LIBRARY_PATH', orig_library_path) setvar('LD_LIBRARY_PATH', orig_ld_library_path) def build_step(self, verbose=False, path=None): @@ -560,30 +530,30 @@ def build_step(self, verbose=False, path=None): if self.cfg['bootstrap']: self.log.info("Building stage 1") print_msg("Building stage 1/3") - change_dir(self.llvm_obj_dir_stage1) - super(EB_LLVMcore, self).build_step(verbose, path) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.1', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + # change_dir(self.llvm_obj_dir_stage1) + # super(EB_LLVMcore, self).build_step(verbose, path) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.1', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - self.configure_step2() - self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.2', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + # self.configure_step2() + # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.2', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") - self.configure_step3() - self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.3', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') + # self.configure_step3() + # self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.3', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') def test_step(self): """Run Clang tests on final stage (unless disabled).""" @@ -592,19 +562,19 @@ def test_step(self): change_dir(basedir) orig_path = os.getenv('PATH') - # orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') # lib_dir = os.path.join(basedir, 'lib') - # lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - # lib_path = ':'.join([lib_dir, os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + lib_path = ':'.join([os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) - # setvar('LD_LIBRARY_PATH', lib_path) + setvar('LD_LIBRARY_PATH', lib_path) - cmd = "make -j 1 check-all" + cmd = "make %s check-all" % self.make_parallel_opts (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) self.log.debug(out) setvar('PATH', orig_path) - # setvar('LD_LIBRARY_PATH', orig_ld_library_path) + setvar('LD_LIBRARY_PATH', orig_ld_library_path) rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) @@ -634,15 +604,13 @@ def install_step(self): os.path.join(basedir, lib_dir_runtime), ]) - _preinstallopts = self. cfg.get('preinstallopts', '') + # _preinstallopts = self.cfg.get('preinstallopts', '') self.cfg.update('preinstallopts', ' '.join([ 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) ])) super(EB_LLVMcore, self).install_step() - self.cfg.update('preinstallopts', _preinstallopts) - def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" arch = get_cpu_architecture() From 3ad9552c21c905fd884bb6de37206185478811f5 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 24 Jun 2024 14:27:39 +0200 Subject: [PATCH 007/103] Working installation + testsuite with `clang-tools-extra` --- easybuild/easyblocks/l/llvmcore.py | 72 +++++++++++++++++++----------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index b6c832ba0d2..c5e5916c1d2 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -179,6 +179,9 @@ # 'CLANG_HAS_GCC_S_LIB': 'Off', 'OPENMP_ENABLE_OMPT_TOOLS': 'Off', + + # Libxml2 from system gets autmatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc + 'LLVM_ENABLE_LIBXML2': 'Off', } disable_werror = { @@ -235,6 +238,10 @@ def extra_options(): def __init__(self, *args, **kwargs): """Initialize LLVM-specific variables.""" super(EB_LLVMcore, self).__init__(*args, **kwargs) + + if LooseVersion(self.version) < LooseVersion('18.1.6'): + raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) + self.llvm_src_dir = None self.llvm_obj_dir_stage1 = None self.llvm_obj_dir_stage2 = None @@ -244,13 +251,6 @@ def __init__(self, *args, **kwargs): self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] self.final_runtimes = [] - if self.cfg['build_runtimes']: - self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] - if self.cfg['build_openmp']: - self.final_projects.append('openmp') - - if LooseVersion(self.version) < LooseVersion('18.1.6'): - raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) # Shared self.build_shared = self.cfg.get('build_shared_libs', False) @@ -286,8 +286,13 @@ def __init__(self, *args, **kwargs): raise EasyBuildError("Full LLVM build requires building lld") if not self.cfg['build_runtimes']: raise EasyBuildError("Full LLVM build requires building runtimes") + if self.cfg['disable_werror']: general_opts.update(disable_werror) + if self.cfg['build_runtimes']: + self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + if self.cfg['build_openmp']: + self.final_projects.append('openmp') if self.cfg['usepolly']: self.final_projects.append('polly') if self.cfg['build_clang_extras']: @@ -353,10 +358,21 @@ def _configure_final_build(self): self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.final_projects) self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.final_runtimes) + hwloc_root = get_software_root('hwloc') + if hwloc_root: + self.cfg.update('configopts', '-DLIBOMP_USE_HWLOC=ON') + self.cfg.update('configopts', '-DLIBOMP_HWLOC_INSTALL_DIR=%s' % hwloc_root) + + if 'openmp' in self.final_projects: + self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' + # Make sure tests are not running with more than `--parallel` tasks self._cmakeopts['LLVM_LIT_ARGS'] = '"-j %s"' % self.cfg['parallel'] if self.cfg['usepolly']: self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' + if not self.cfg['skip_all_tests']: + self._cmakeopts['LLVM_INCLUDE_TESTS'] = 'ON' + self._cmakeopts['LLVM_BUILD_TESTS'] = 'ON' def configure_step(self): """ @@ -400,6 +416,12 @@ def configure_step(self): self.log.debug("Disabling the sanitizer tests") self.disable_sanitizer_tests() + # Remove python bindings tests causing uncaught exception in the build + cmakelists_tests = os.path.join(self.llvm_src_dir, 'clang', 'CMakeLists.txt') + regex_subs = [] + regex_subs.append((r'add_subdirectory\(bindings/python/tests\)', '')) + apply_regex_substitutions(cmakelists_tests, regex_subs) + gcc_prefix = get_software_root('GCCcore') # If that doesn't work, try with GCC if gcc_prefix is None: @@ -530,30 +552,30 @@ def build_step(self, verbose=False, path=None): if self.cfg['bootstrap']: self.log.info("Building stage 1") print_msg("Building stage 1/3") - # change_dir(self.llvm_obj_dir_stage1) - # super(EB_LLVMcore, self).build_step(verbose, path) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.1', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVMcore, self).build_step(verbose, path) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.1', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - # self.configure_step2() - # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.2', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + self.configure_step2() + self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.2', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") - # self.configure_step3() - # self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.3', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') + self.configure_step3() + self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.3', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') def test_step(self): """Run Clang tests on final stage (unless disabled).""" From 9d459b0779c72adac1f86b766076f844d6a64f6e Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 24 Jun 2024 14:34:14 +0200 Subject: [PATCH 008/103] linting --- easybuild/easyblocks/l/llvmcore.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index c5e5916c1d2..0e48799fce7 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -32,7 +32,6 @@ import glob import os import re -import shutil from easybuild.framework.easyconfig import CUSTOM from easybuild.tools import LooseVersion, run @@ -204,6 +203,7 @@ # 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html } + class EB_LLVMcore(CMakeMake): """ Support for building and installing LLVM @@ -347,7 +347,6 @@ def _configure_general_build(self): self._cmakeopts.update(general_opts) - def _configure_intermediate_build(self): """Configure the intermediate stages of the build.""" self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) @@ -454,7 +453,7 @@ def disable_sanitizer_tests(self): def add_cmake_opts(self): """Add LLVM-specific CMake options.""" base_opts = self._cfgopts.copy() - for k,v in self._cmakeopts.items(): + for k, v in self._cmakeopts.items(): base_opts.append('-D%s=%s' % (k, v)) self.cfg['configopts'] = ' '.join(base_opts) @@ -640,7 +639,7 @@ def get_runtime_lib_path(self, base_dir, fail_ok=True): matches = glob.glob(glob_pattern) if matches: directory = os.path.basename(matches[0]) - res = os.path.join("lib", directory) + res = os.path.join("lib", directory) else: if not fail_ok: raise EasyBuildError("Could not find runtime library directory") From affaf33b34ba8107bd6f96ec2d059411ba01de19 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 25 Jun 2024 10:18:58 +0200 Subject: [PATCH 009/103] Reduced errors and reimplemented OMPT_TOOLS --- easybuild/easyblocks/l/llvmcore.py | 85 +++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 0e48799fce7..fe8caf57735 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -32,6 +32,7 @@ import glob import os import re +import shutil from easybuild.framework.easyconfig import CUSTOM from easybuild.tools import LooseVersion, run @@ -177,7 +178,7 @@ # 'COMPILER_RT_HAS_GCC_S_LIB': 'Off', # 'CLANG_HAS_GCC_S_LIB': 'Off', - 'OPENMP_ENABLE_OMPT_TOOLS': 'Off', + # 'OPENMP_ENABLE_OMPT_TOOLS': 'Off', # Libxml2 from system gets autmatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc 'LLVM_ENABLE_LIBXML2': 'Off', @@ -246,7 +247,6 @@ def __init__(self, *args, **kwargs): self.llvm_obj_dir_stage1 = None self.llvm_obj_dir_stage2 = None self.llvm_obj_dir_stage3 = None - # self.llvm_obj_dir_stage4 = None self.intermediate_projects = ['llvm', 'clang'] self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] @@ -278,8 +278,10 @@ def __init__(self, *args, **kwargs): # Does not work with Flang # general_opts['LLVM_ENABLE_EH'] = 'ON' + self.full_llvm = self.cfg['full_llvm'] + # Other vustom options - if self.cfg['full_llvm']: + if self.full_llvm: if not self.cfg['bootstrap']: raise EasyBuildError("Full LLVM build irequires bootstrap build") if not self.cfg['build_lld']: @@ -302,6 +304,9 @@ def __init__(self, *args, **kwargs): self.final_projects.append('lld') if self.cfg['build_lldb']: self.final_projects.append('lldb') + if self.full_llvm: + remove_gcc_opts['LLDB_ENABLE_LIBXML2'] = 'Off' + remove_gcc_opts['LLDB_ENABLE_LZMA'] = 'Off' if self.cfg['build_bolt']: self.final_projects.append('bolt') @@ -321,6 +326,7 @@ def __init__(self, *args, **kwargs): if unknown_targets: raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", ', '.join(unknown_targets), ', '.join(CLANG_TARGETS)) + self.build_targets = build_targets or [] general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir @@ -359,8 +365,9 @@ def _configure_final_build(self): hwloc_root = get_software_root('hwloc') if hwloc_root: - self.cfg.update('configopts', '-DLIBOMP_USE_HWLOC=ON') - self.cfg.update('configopts', '-DLIBOMP_HWLOC_INSTALL_DIR=%s' % hwloc_root) + self.log.info("Using %s as hwloc root", hwloc_root) + self._cmakeopts['LIBOMP_USE_HWLOC'] = 'ON' + self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root if 'openmp' in self.final_projects: self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' @@ -395,8 +402,6 @@ def configure_step(self): self.log.info("Initialising for single stage build.") self.final_dir = self.llvm_obj_dir_stage1 - # return - gcc_version = get_software_version('GCCcore') if LooseVersion(gcc_version) < LooseVersion('13'): raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) @@ -404,7 +409,7 @@ def configure_step(self): self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: self._configure_intermediate_build() - # if self.cfg['full_llvm']: + # if self.full_llvm: # self.intermediate_projects.append('libc') # self.final_projects.append('libc') else: @@ -431,6 +436,46 @@ def configure_step(self): self._cmakeopts['GCC_INSTALL_PREFIX'] = gcc_prefix self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) + # Disable OpenMP offload support if not building for NVPTX or AMDGPU + if 'NVPTX' not in self.build_targets and 'AMDGPU' not in self.build_targets: + general_opts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' + + # If 'NVPTX' is in the build targets we assume the user would like OpenMP offload support as well + if 'NVPTX' in self.build_targets: + # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): + # (1) in the easyconfig file, via the custom cuda_compute_capabilities; + # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; + ec_cuda_cc = self.cfg['cuda_compute_capabilities'] + cfg_cuda_cc = build_option('cuda_compute_capabilities') + cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] + if not cuda_cc: + raise EasyBuildError("Can't build Clang with CUDA support " + "without specifying 'cuda-compute-capabilities'") + default_cc = self.cfg['default_cuda_capability'] or min(cuda_cc) + if not self.cfg['default_cuda_capability']: + print_warning("No default CUDA capability defined! " + "Using '%s' taken as minimum from 'cuda_compute_capabilities'" % default_cc) + cuda_cc = [cc.replace('.', '') for cc in cuda_cc] + default_cc = default_cc.replace('.', '') + general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = 'sm_%s' % default_cc + general_opts['CLANG_OPENMP_NVPTX_DEFAULT_ARCH'] = 'sm_%s' % default_cc + general_opts['LIBOMPTARGET_NVPTX_COMPUTE_CAPABILITIES'] = ','.join(cuda_cc) + # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by + # using the environment variable which is used as-is and later checked for a falsy value when determining + # whether CUDA was found + if not get_software_root('CUDA'): + setvar('CUDA_NVCC_EXECUTABLE', 'IGNORE') + # If 'AMDGPU' is in the build targets we assume the user would like OpenMP offload support for AMD + if 'AMDGPU' in self.build_targets: + if not get_software_root('ROCR-Runtime'): + raise EasyBuildError("Can't build Clang with AMDGPU support " + "without dependency 'ROCR-Runtime'") + ec_amdgfx = self.cfg['amd_gfx_list'] + if not ec_amdgfx: + raise EasyBuildError("Can't build Clang with AMDGPU support " + "without specifying 'amd_gfx_list'") + general_opts['LIBOMPTARGET_AMDGCN_GFXLIST'] = ' '.join(ec_amdgfx) + self._configure_general_build() self.add_cmake_opts() @@ -457,17 +502,17 @@ def add_cmake_opts(self): base_opts.append('-D%s=%s' % (k, v)) self.cfg['configopts'] = ' '.join(base_opts) - self.log.debug("-%"*50) - self.log.debug("Using %s as configopts", self._cfgopts) - self.log.debug("Using %s as cmakeopts", self._cmakeopts) - self.log.debug("-%"*50) + # self.log.debug("-%"*50) + # self.log.debug("Using %s as configopts", self._cfgopts) + # self.log.debug("Using %s as cmakeopts", self._cmakeopts) + # self.log.debug("-%"*50) def configure_step2(self): """Configure the second stage of the bootstrap.""" self._cmakeopts = {} self._configure_general_build() self._configure_intermediate_build() - if self.cfg['full_llvm']: + if self.full_llvm: self._cmakeopts.update(remove_gcc_opts) def configure_step3(self): @@ -475,7 +520,7 @@ def configure_step3(self): self._cmakeopts = {} self._configure_general_build() self._configure_final_build() - if self.cfg['full_llvm']: + if self.full_llvm: self._cmakeopts.update(remove_gcc_opts) def build_with_prev_stage(self, prev_dir, stage_dir): @@ -493,8 +538,6 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self.add_cmake_opts() bin_dir = os.path.join(prev_dir, 'bin') - # prev_lib_dir = os.path.join(prev_dir, 'lib') - # curr_lib_dir = os.path.join(stage_dir, 'lib') lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols @@ -551,6 +594,9 @@ def build_step(self, verbose=False, path=None): if self.cfg['bootstrap']: self.log.info("Building stage 1") print_msg("Building stage 1/3") + else: + self.log.info("Building LLVM") + print_msg("Building stage 1/1") change_dir(self.llvm_obj_dir_stage1) super(EB_LLVMcore, self).build_step(verbose, path) # change_dir(self.builddir) @@ -649,7 +695,7 @@ def get_runtime_lib_path(self, base_dir, fail_ok=True): return res def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): - self.runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) + self.get_runtime_lib_path(self.installdir, fail_ok=False) shlib_ext = get_shared_lib_ext() if self.build_shared: @@ -675,10 +721,11 @@ def make_module_req_guess(self): """ Clang can find its own headers and libraries but the .so's need to be in LD_LIBRARY_PATH """ + runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) guesses = super(EB_LLVMcore, self).make_module_req_guess() guesses.update({ 'CPATH': [], - 'LIBRARY_PATH': ['lib', 'lib64', self.runtime_lib_path], - 'LD_LIBRARY_PATH': ['lib', 'lib64', self.runtime_lib_path], + 'LIBRARY_PATH': ['lib', 'lib64', runtime_lib_path], + 'LD_LIBRARY_PATH': ['lib', 'lib64', runtime_lib_path], }) return guesses From 85359ae6c920818dc0aaf052be9e65486b3aebcb Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 25 Jun 2024 10:24:37 +0200 Subject: [PATCH 010/103] Removing python dep from `lldb` in `full_llvm` mode --- easybuild/easyblocks/l/llvmcore.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index fe8caf57735..ef50c145eba 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -307,6 +307,7 @@ def __init__(self, *args, **kwargs): if self.full_llvm: remove_gcc_opts['LLDB_ENABLE_LIBXML2'] = 'Off' remove_gcc_opts['LLDB_ENABLE_LZMA'] = 'Off' + remove_gcc_opts['LLDB_ENABLE_PYTHON'] = 'Off' if self.cfg['build_bolt']: self.final_projects.append('bolt') From e25717a30acf837447446fdcb70d24c9a3041001 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 25 Jun 2024 16:17:17 +0200 Subject: [PATCH 011/103] Added more checks --- easybuild/easyblocks/l/llvmcore.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index ef50c145eba..653f3f66d45 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -288,6 +288,7 @@ def __init__(self, *args, **kwargs): raise EasyBuildError("Full LLVM build requires building lld") if not self.cfg['build_runtimes']: raise EasyBuildError("Full LLVM build requires building runtimes") + self.log.info("Building LLVM without any GCC dependency") if self.cfg['disable_werror']: general_opts.update(disable_werror) @@ -403,6 +404,20 @@ def configure_step(self): self.log.info("Initialising for single stage build.") self.final_dir = self.llvm_obj_dir_stage1 + xml2_root = get_software_root('libxml2') + if xml2_root: + if self.full_llvm: + self.log.warning("LLVM is being built in `full_llvm` mode, libxml2 will not be used") + else: + self._cmakeopts['LLVM_ENABLE_LIBXML2'] = 'ON' + # self._cmakeopts['LIBXML2_ROOT'] = xml2_root + + lit_root = get_software_root('lit') + if not lit_root: + if not self.cfg['skip_all_tests']: + raise EasyBuildError("Can't find `lit`, needed for running tests-suite") + + gcc_version = get_software_version('GCCcore') if LooseVersion(gcc_version) < LooseVersion('13'): raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) @@ -459,6 +474,7 @@ def configure_step(self): cuda_cc = [cc.replace('.', '') for cc in cuda_cc] default_cc = default_cc.replace('.', '') general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = 'sm_%s' % default_cc + # OLD (pre v16) flags general_opts['CLANG_OPENMP_NVPTX_DEFAULT_ARCH'] = 'sm_%s' % default_cc general_opts['LIBOMPTARGET_NVPTX_COMPUTE_CAPABILITIES'] = ','.join(cuda_cc) # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by From 5b68ae041b0ac71a217376319db31add8eb256cd Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 3 Jul 2024 10:22:04 +0200 Subject: [PATCH 012/103] Re-running test-suite with 1 proc seems to work in a non bootstrapped build --- easybuild/easyblocks/l/llvmcore.py | 70 ++++++++++++++++++------------ 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 653f3f66d45..1fe66cd24ac 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -639,37 +639,51 @@ def build_step(self, verbose=False, path=None): # shutil.rmtree('llvm.obj.3', ignore_errors=True) # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') + def _test_step(self, parallel=1): + """Run test suite with the specified number of parallel jobs for make.""" + basedir = self.final_dir + + change_dir(basedir) + orig_path = os.getenv('PATH') + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + # lib_dir = os.path.join(basedir, 'lib') + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + lib_path = ':'.join([os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) + setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) + setvar('LD_LIBRARY_PATH', lib_path) + + cmd = "make -j %s check-all" % parallel + (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + self.log.debug(out) + + setvar('PATH', orig_path) + setvar('LD_LIBRARY_PATH', orig_ld_library_path) + + rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_failed.search(out) + if mch is None: + rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_passed.search(out) + if mch is None: + num_failed = None + else: + num_failed = 0 + else: + num_failed = int(mch.group(1)) + + return num_failed + def test_step(self): """Run Clang tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: - basedir = self.final_dir - - change_dir(basedir) - orig_path = os.getenv('PATH') - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - # lib_dir = os.path.join(basedir, 'lib') - lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - lib_path = ':'.join([os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) - setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) - setvar('LD_LIBRARY_PATH', lib_path) - - cmd = "make %s check-all" % self.make_parallel_opts - (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) - self.log.debug(out) - - setvar('PATH', orig_path) - setvar('LD_LIBRARY_PATH', orig_ld_library_path) - - rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) - mch = rgx_failed.search(out) - if mch is None: - rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) - mch = rgx_passed.search(out) - if mch is None: - raise EasyBuildError("Failed to extract test results from output") - num_failed = 0 - else: - num_failed = int(mch.group(1)) + self.log.info("Running test-suite with parallel jobs") + num_failed = self._test_step(parallel=self.cfg['parallel']) + if num_failed is None: + self.log.warning("Tests with parallel jobs failed, retrying with single job") + num_failed = self._test_step(parallel=1) + if num_failed is None: + raise EasyBuildError("Failed to extract test results from output") + if num_failed > self.cfg['test_suite_max_failed']: raise EasyBuildError("Too many failed tests: %s", num_failed) From 924c1e0db9794567a16929d68249b019b1e22e6f Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 3 Jul 2024 10:25:56 +0200 Subject: [PATCH 013/103] Cleanup unused comments --- easybuild/easyblocks/l/llvmcore.py | 104 ----------------------------- 1 file changed, 104 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 1fe66cd24ac..54fd88f984d 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -50,100 +50,7 @@ from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP from easybuild.easyblocks.generic.cmakemake import CMakeMake -# remove_gcc_opts = { -# 'LIBCXX_CXX_ABI': 'libcxxabi', -# 'LIBCXX_USE_COMPILER_RT': 'On', -# 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', -# 'LIBCXXABI_USE_COMPILER_RT': 'On', -# 'LIBCXX_HAS_GCC_S_LIB': 'Off', -# 'LIBUNWIND_USE_COMPILER_RT': 'On', -# 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', -# 'CLANG_DEFAULT_RTLIB': 'compiler-rt', -# } - -# https://discourse.llvm.org/t/should-buildbots-switch-to-enable-runtimes-instead-of-enable-projects-for-compiler-rt/65042/12 -# cmake -# ‘-DCMAKE_PREFIX_PATH=/home/ampandey/rocm-toolchain/rocm-5.6/llvm;/home/ampandey/rocm-toolchain/rocm-5.6’ -# -DCMAKE_BUILD_TYPE=Release -# -DCMAKE_VERBOSE_MAKEFILE=1 -# -DCPACK_GENERATOR=DEB -# ‘-DCMAKE_INSTALL_RPATH=$ORIGIN:$ORIGIN/…/lib:$ORIGIN/…/lib64:/opt/rocm-5.6.0-9999/lib:/opt/rocm-5.6.0-9999/lib64:/opt/rocm/lib:/opt/rocm/lib64:$ORIGIN/…/llvm/lib’ -# -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE -# -DROCM_PATCH_VERSION=50600 -# -DCMAKE_INSTALL_PREFIX=/home/ampandey/rocm-toolchain/rocm-5.6 -# -DCPACK_PACKAGING_INSTALL_PREFIX=/home/ampandey/rocm-toolchain/rocm-5.6 -# -DCMAKE_INSTALL_PREFIX=/home/ampandey/rocm-toolchain/rocm-5.6/llvm -# ‘-DLLVM_TARGETS_TO_BUILD=AMDGPU;X86’ -# ‘-DLLVM_ENABLE_PROJECTS=clang;lld;clang-tools-extra’ -# ‘-DLLVM_ENABLE_RUNTIMES=libcxx;libcxxabi;compiler-rt’ -# -DLIBCXX_ENABLE_SHARED=OFF -# -DLIBCXX_ENABLE_STATIC=ON -# -DLIBCXX_INSTALL_LIBRARY=OFF -# -DLIBCXX_INSTALL_HEADERS=OFF -# -DLIBCXXABI_ENABLE_SHARED=OFF -# -DLIBCXXABI_ENABLE_STATIC=ON -# -DLIBCXXABI_INSTALL_STATIC_LIBRARY=OFF -# -DLLVM_BUILD_DOCS=OFF -# -DLLVM_ENABLE_SPHINX=OFF -# -DSPHINX_WARNINGS_AS_ERRORS=OFF -# -DSPHINX_OUTPUT_MAN=OFF -# -DLLVM_ENABLE_ASSERTIONS=1 -# -DLLVM_ENABLE_Z3_SOLVER=OFF -# -DLLVM_ENABLE_ZLIB=ON -# -DLLVM_AMDGPU_ALLOW_NPI_TARGETS=ON -# -DCLANG_DEFAULT_PIE_ON_LINUX=0 -# -DCLANG_DEFAULT_LINKER=lld -# -DCLANG_DEFAULT_RTLIB=compiler-rt -# -DCLANG_DEFAULT_UNWINDLIB=libgcc -# -DPACKAGE_VENDOR=AMD -# -DLLVM_BUILD_LLVM_DYLIB=OFF -# -DLLVM_LINK_LLVM_DYLIB=OFF -# -DLLVM_ENABLE_LIBCXX=OFF -# /home/ampandey/rocm-toolchain/build/…/external/llvm-project/llvm - - -# https://github.com/llvm/llvm-project/issues/72108 -# -DCMAKE_BUILD_TYPE="Release" \ -# -DCMAKE_C_FLAGS="-Wno-backend-plugin" \ -# -DCMAKE_CXX_FLAGS="-Wno-backend-plugin" \ -# -DCMAKE_C_COMPILER="clang" \ -# -DCMAKE_CXX_COMPILER="clang++" \ -# -DLLVM_ENABLE_CLASSIC_FLANG=ON \ -# -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;openmp" \ -# -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi" \ -# -DLLVM_LIBDIR_SUFFIX=64 \ -# -DLLVM_USE_LINKER=lld \ -# -DLLVM_PARALLEL_COMPILE_JOBS="96" \ -# -DLLVM_PARALLEL_LINK_JOBS=96 \ -# -DLLVM_ENABLE_NEW_PASS_MANAGER=ON \ -# -DLLVM_TARGETS_TO_BUILD="X86;BPF;WebAssembly" \ -# -DLLVM_ENABLE_LIBCXX=OFF \ -# -DLLVM_STATIC_LINK_CXX_STDLIB=OFF \ -# -DLLVM_BINUTILS_INCDIR="/usr/include" \ -# -DLLVM_ENABLE_TERMINFO=OFF \ -# -DLLVM_ENABLE_LIBXML2=OFF \ -# -DLLVM_ENABLE_LIBEDIT=OFF \ -# -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF \ -# -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ -# -DCOMPILER_RT_BUILD_XRAY=OFF \ -# -DCOMPILER_RT_BUILD_ORC=OFF \ -# -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \ -# -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=OFF \ -# -DOPENMP_ENABLE_LIBOMPTARGET=OFF \ -# -DOPENMP_ENABLE_OMPT_TOOLS=OFF \ -# -DOPENMP_ENABLE_TESTING=OFF \ -# -DCOMPILER_RT_CXX_LIBRARY=libcxx \ -# -DSANITIZER_CXX_ABI=libc++ \ -# -DSANITIZER_CXX_ABI_INTREE=ON \ -# -DSANITIZER_USE_STATIC_CXX_ABI=ON \ -# -DSANITIZER_TEST_CXX=libc++ \ -# -DSANITIZER_TEST_CXX_INTREE=ON \ -# -DSANITIZER_USE_STATIC_TEST_CXX=ON \ -# -DSANITIZER_LIT_USE_LIBCXX=ON \ - remove_gcc_opts = { - # 'LLVM_LIBC_FULL_BUILD': 'On', - # 'LLVM_LIBC_INCLUDE_SCUDO': 'On', 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', @@ -154,18 +61,12 @@ 'LIBUNWIND_USE_COMPILER_RT': 'On', 'SANITIZER_USE_STATIC_LLVM_UNWINDER': 'On', - # 'SANITIZER_USE_STATIC_CXX_ABI': 'On', 'COMPILER_RT_USE_LIBCXX': 'On', 'COMPILER_RT_USE_LLVM_UNWINDER': 'On', - # 'COMPILER_RT_CXX_LIBRARY': 'libcxx', 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', - # 'COMPILER_RT_BUILD_SCUDO_STANDALONE_WITH_LLVM_LIBC': 'On', - # 'COMPILER_RT_SCUDO_STANDALONE_BUILD_SHARED': 'Off', - # Required for building the standalone libatomic (not depending on GCCcore) - # 'COMPILER_RT_BUILD_STANDALONE_LIBATOMIC': 'On', 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', 'CLANG_DEFAULT_RTLIB': 'compiler-rt', @@ -175,10 +76,6 @@ 'LIBCXX_HAS_GCC_S_LIB': 'Off', 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', - # 'COMPILER_RT_HAS_GCC_S_LIB': 'Off', - # 'CLANG_HAS_GCC_S_LIB': 'Off', - - # 'OPENMP_ENABLE_OMPT_TOOLS': 'Off', # Libxml2 from system gets autmatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc 'LLVM_ENABLE_LIBXML2': 'Off', @@ -201,7 +98,6 @@ 'LLVM_INSTALL_UTILS': 'ON', 'LLVM_INCLUDE_BENCHMARKS': 'OFF', 'CMAKE_VERBOSE_MAKEFILE': 'ON', - # 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html } From 044657f492fda183cc8c2eb5fde9d242a56e31d3 Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 3 Jul 2024 10:26:58 +0200 Subject: [PATCH 014/103] Linting --- easybuild/easyblocks/l/llvmcore.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 54fd88f984d..788d75e6a12 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -32,7 +32,6 @@ import glob import os import re -import shutil from easybuild.framework.easyconfig import CUSTOM from easybuild.tools import LooseVersion, run @@ -313,7 +312,6 @@ def configure_step(self): if not self.cfg['skip_all_tests']: raise EasyBuildError("Can't find `lit`, needed for running tests-suite") - gcc_version = get_software_version('GCCcore') if LooseVersion(gcc_version) < LooseVersion('13'): raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) @@ -512,6 +510,7 @@ def build_step(self, verbose=False, path=None): print_msg("Building stage 1/1") change_dir(self.llvm_obj_dir_stage1) super(EB_LLVMcore, self).build_step(verbose, path) + # import shutil # change_dir(self.builddir) # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") # shutil.rmtree('llvm.obj.1', ignore_errors=True) From 59ab5de774f0790859b7f37519cbc61bb4492368 Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 3 Jul 2024 10:28:32 +0200 Subject: [PATCH 015/103] rename `remove_gcc_opts` to `remove_gcc_dependency_opts` --- easybuild/easyblocks/l/llvmcore.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 788d75e6a12..659b54af050 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -49,8 +49,7 @@ from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP from easybuild.easyblocks.generic.cmakemake import CMakeMake -remove_gcc_opts = { - +remove_gcc_dependency_opts = { 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', @@ -201,9 +200,9 @@ def __init__(self, *args, **kwargs): if self.cfg['build_lldb']: self.final_projects.append('lldb') if self.full_llvm: - remove_gcc_opts['LLDB_ENABLE_LIBXML2'] = 'Off' - remove_gcc_opts['LLDB_ENABLE_LZMA'] = 'Off' - remove_gcc_opts['LLDB_ENABLE_PYTHON'] = 'Off' + remove_gcc_dependency_opts['LLDB_ENABLE_LIBXML2'] = 'Off' + remove_gcc_dependency_opts['LLDB_ENABLE_LZMA'] = 'Off' + remove_gcc_dependency_opts['LLDB_ENABLE_PYTHON'] = 'Off' if self.cfg['build_bolt']: self.final_projects.append('bolt') @@ -413,18 +412,13 @@ def add_cmake_opts(self): base_opts.append('-D%s=%s' % (k, v)) self.cfg['configopts'] = ' '.join(base_opts) - # self.log.debug("-%"*50) - # self.log.debug("Using %s as configopts", self._cfgopts) - # self.log.debug("Using %s as cmakeopts", self._cmakeopts) - # self.log.debug("-%"*50) - def configure_step2(self): """Configure the second stage of the bootstrap.""" self._cmakeopts = {} self._configure_general_build() self._configure_intermediate_build() if self.full_llvm: - self._cmakeopts.update(remove_gcc_opts) + self._cmakeopts.update(remove_gcc_dependency_opts) def configure_step3(self): """Configure the third stage of the bootstrap.""" @@ -432,7 +426,7 @@ def configure_step3(self): self._configure_general_build() self._configure_final_build() if self.full_llvm: - self._cmakeopts.update(remove_gcc_opts) + self._cmakeopts.update(remove_gcc_dependency_opts) def build_with_prev_stage(self, prev_dir, stage_dir): """Build LLVM using the previous stage.""" From a7175926fabf66838a44c33864c16220eb8faed4 Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 3 Jul 2024 10:33:15 +0200 Subject: [PATCH 016/103] Added `build_openmp_tools` option --- easybuild/easyblocks/l/llvmcore.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 659b54af050..a7a0b40649a 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -123,6 +123,7 @@ def extra_options(): 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], 'build_runtimes': [True, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], + 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], @@ -190,6 +191,9 @@ def __init__(self, *args, **kwargs): self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] if self.cfg['build_openmp']: self.final_projects.append('openmp') + if self.cfg['build_openmp_tools']: + if not self.cfg['build_openmp']: + raise EasyBuildError("Building OpenMP tools requires building OpenMP runtime") if self.cfg['usepolly']: self.final_projects.append('polly') if self.cfg['build_clang_extras']: @@ -267,6 +271,8 @@ def _configure_final_build(self): if 'openmp' in self.final_projects: self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' + if not self.cfg['build_openmp_tools']: + self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' # Make sure tests are not running with more than `--parallel` tasks self._cmakeopts['LLVM_LIT_ARGS'] = '"-j %s"' % self.cfg['parallel'] From c9d32d53c21b277229d29a05afe85abb0b9c4c70 Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 3 Jul 2024 12:05:21 +0200 Subject: [PATCH 017/103] Added installation of python bindings --- easybuild/easyblocks/l/llvmcore.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index a7a0b40649a..db3f0bf0c56 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -32,6 +32,7 @@ import glob import os import re +import shutil from easybuild.framework.easyconfig import CUSTOM from easybuild.tools import LooseVersion, run @@ -604,6 +605,19 @@ def install_step(self): super(EB_LLVMcore, self).install_step() + def post_install_step(self): + """Install python bindings.""" + super(EB_LLVMcore, self).post_install_step() + + # copy Python bindings here in post-install step so that it is not done more than once in multi_deps context + if self.cfg['python_bindings'] and self.project_name in ['clang', 'mlir']: + python_bindings_source_dir = os.path.join(self.llvm_src_dir, "llvm", "bindings", "python") + python_bindins_target_dir = os.path.join(self.installdir, 'lib', 'python') + shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) + + python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") + shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) + def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" arch = get_cpu_architecture() From 9d0d2bf7e34e8a5da5d63e0ec039bafd0d04e341 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 11:49:35 +0200 Subject: [PATCH 018/103] Makes sure a static build is also successful --- easybuild/easyblocks/l/llvmcore.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index db3f0bf0c56..ad3dba7ae27 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -53,6 +53,7 @@ remove_gcc_dependency_opts = { 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', + 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', 'LIBCXXABI_USE_COMPILER_RT': 'On', @@ -164,6 +165,8 @@ def __init__(self, *args, **kwargs): general_opts['LIBCXXABI_ENABLE_SHARED'] = 'OFF' general_opts['LIBUNWIND_ENABLE_SHARED'] = 'OFF' general_opts['LIBCXX_ENABLE_STATIC'] = 'ON' + general_opts['LIBCXX_ENABLE_STATIC_ABI_LIBRARY'] = 'ON' + general_opts['LIBCXX_ENABLE_ABI_LINKER_SCRIPT'] = 'OFF' general_opts['LIBCXXABI_ENABLE_STATIC'] = 'ON' general_opts['LIBUNWIND_ENABLE_STATIC'] = 'ON' @@ -634,6 +637,16 @@ def get_runtime_lib_path(self, base_dir, fail_ok=True): return res + def banned_linked_shared_libs(self): + """Return a list of shared libraries that should not be linked against.""" + res = [] + if self.full_llvm: + res += ['libstdc++', 'libgcc_s', 'libicuuc'] + if not self.build_shared: + # Libraries should be linked statically + res += ['libc++', 'libc++abi', 'libunwind'] + return res + def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): self.get_runtime_lib_path(self.installdir, fail_ok=False) shlib_ext = get_shared_lib_ext() From fc17b63a6196d55dbb6e9dd1fc4a9a1d9fd930be Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 12:04:50 +0200 Subject: [PATCH 019/103] Improved sanity checks --- easybuild/easyblocks/l/llvmcore.py | 133 +++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index ad3dba7ae27..e1cb99c8644 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -648,17 +648,134 @@ def banned_linked_shared_libs(self): return res def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): - self.get_runtime_lib_path(self.installdir, fail_ok=False) - shlib_ext = get_shared_lib_ext() + lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) + shlib_ext = '.' + get_shared_lib_ext() + + resdir_version = self.version.split('.')[0] + + check_files = [] + check_bin_files = [] + check_lib_files = [] + check_inc_files = [] + check_dirs = ['include/llvm', 'include/llvm-c', 'lib/cmake/llvm'] + custom_commands = [ + 'llvm-ar --help', 'llvm-ranlib --help', 'llvm-nm --help', 'llvm-objdump --help', + 'llvm-config --cxxflags', 'clang --help', 'clang++ --help', + 'bbc --help', 'mlir-tblgen --help', 'flang-new --help', + ] if self.build_shared: - if custom_paths is None: - custom_paths = {} - ptr = custom_paths.setdefault('files', []) - for lib in ['LLVM', 'MLIR', 'clang', 'clang-cpp', 'lldb']: - ptr.append(os.path.join('lib', 'lib%s.%s' % (lib, shlib_ext))) + check_lib_files += ['libLLVM.so'] + + if 'clang' in self.final_projects: + check_bin_files += [ + 'clang', 'clang++', 'clang-cpp', 'clang-cl', 'clang-repl', 'hmaptool', 'amdgpu-arch', 'nvptx-arch', + 'intercept-build', 'scan-build', 'scan-build-py', 'scan-view', 'analyze-build', 'c-index-test', + 'clang-tblgen', + ] + check_lib_files += [ + 'libclang.so', 'libclang-cpp.so', 'libclangAST.a', 'libclangCrossTU.a', 'libclangFrontend.a', + 'libclangInterpreter.a', 'libclangParse.a', 'libclangTooling.a' + ] + check_dirs += [ + 'lib/cmake/clang', 'include/clang' + ] + if 'clang-tools-extra' in self.final_projects: + check_bin_files += [ + 'clangd', 'clang-tidy', 'clang-pseudo', 'clang-include-fixer', 'clang-query', 'clang-move', + 'clang-reorder-fields', 'clang-include-cleaner', 'clang-apply-replacements', + 'clang-change-namespace', 'pp-trace', 'modularize' + ] + check_lib_files += [ + 'libclangTidy.a', 'libclangQuery.a', 'libclangIncludeFixer.a', 'libclangIncludeCleaner.a', + ] + check_dirs += ['include/clang-tidy'] + if 'flang' in self.final_projects: + check_bin_files += ['bbc', 'flang-new', 'flang-to-external-fc', 'f18-parse-demo', 'fir-opt', 'tco'] + check_lib_files += [ + 'libFortranRuntime.a', 'libFortranSemantics.a', 'libFortranLower.a', 'libFortranParser.a', + 'libFIRCodeGen.a', 'libflangFrontend.a', 'libFortranCommon.a', 'libFortranDecimal.a', + 'libHLFIRDialect.a' + ] + check_dirs += ['lib/cmake/flang', 'include/flang'] + if 'lld' in self.final_projects: + check_bin_files += ['ld.lld', 'lld', 'lld-link', 'wasm-ld'] + check_lib_files += [ + 'liblldCOFF.a', 'liblldCommon.a', 'liblldELF.a', 'liblldMachO.a', 'liblldMinGW.a', 'liblldWasm.a' + ] + check_dirs += ['lib/cmake/lld', 'include/lld'] + if 'lldb' in self.final_projects: + check_bin_files += ['lldb'] + if self.build_shared: + check_lib_files += ['liblldb.so'] + check_dirs += ['include/lldb'] + if 'mlir' in self.final_projects: + check_bin_files += ['mlir-opt', 'tblgen-to-irdl', 'mlir-pdll'] + check_lib_files += [ + 'libMLIRIR.a', 'libmlir_async_runtime.so', 'libmlir_arm_runner_utils.so', 'libmlir_c_runner_utils.so', + 'libmlir_float16_utils.so' + ] + check_dirs += ['lib/cmake/mlir', 'include/mlir', 'include/mlir-c'] + if 'compiler-rt' in self.final_runtimes: + pth = os.path.join('lib', 'clang', resdir_version, lib_dir_runtime) + # check_files += [os.path.join(pth, _) for _ in [ + # # This should probably be more finetuned depending on what features of compiler-rt are used + # 'libclang_rt.xray.a', 'libclang_rt.fuzzer.a', 'libclang_rt.gwp_asan.a', 'libclang_rt.profile.a', + # 'libclang_rt.lsan.a', 'libclang_rt.asan.a', 'libclang_rt.hwasan.a' + # ]] + # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] + if 'libunwind' in self.final_runtimes: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.a']] + if self.build_shared: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.so']] + check_inc_files += ['unwind.h', 'libunwind.h', 'mach-o/compact_unwind_encoding.h'] + if 'libcxx' in self.final_runtimes: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.a']] + if self.build_shared: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.so']] + check_dirs += ['include/c++/v1'] + if 'libcxxabi' in self.final_runtimes: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.a']] + if self.build_shared: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.so']] + + if 'polly' in self.final_projects: + check_lib_files += ['libPolly.a', 'libPollyISL.a'] + if self.build_shared: + check_lib_files += ['libPolly.so'] + check_dirs += ['lib/cmake/polly', 'include/polly'] + custom_commands += [ + ' | '.join([ + 'echo \'int main(int argc, char **argv) { return 0; }\'', + 'clang -xc -O3 -mllvm -polly -' + ]) + ' && ./a.out && rm -f a.out' + ] + if 'bolt' in self.final_projects: + check_bin_files += ['llvm-bolt', 'llvm-boltdiff', 'llvm-bolt-heatmap'] + check_lib_files += ['libbolt_rt_instr.a'] + if 'openmp' in self.final_projects: + check_lib_files += ['libomp.so', 'libompd.so'] + if self.cfg['build_openmp_tools']: + check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] + if self.cfg['python_bindings']: + custom_commands += ["python -c 'import clang'"] + + for libso in filter(lambda x: x.endswith('.so'), check_lib_files): + libext = libso.replace('.so', shlib_ext) + if libext not in check_lib_files: + check_lib_files.append(libext) + check_lib_files.remove(libso) + + check_files += [os.path.join('bin', _) for _ in check_bin_files] + check_files += [os.path.join('lib', _) for _ in check_lib_files] + check_files += [os.path.join('include', _) for _ in check_inc_files] + + custom_paths = { + 'files': check_files, + 'dirs': check_dirs, + } - return super().sanity_check_step(custom_paths=None, custom_commands=None, extension=False, extra_modules=None) + return super().sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) def make_module_extra(self): """Custom variables for Clang module.""" From 22e8257fd0fab89c2baacdf5299003b246c10103 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 12:05:36 +0200 Subject: [PATCH 020/103] Reelocated OPENMP_ENABLE_LIBOMPTARGET check --- easybuild/easyblocks/l/llvmcore.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index e1cb99c8644..174259771fd 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -274,6 +274,9 @@ def _configure_final_build(self): self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root if 'openmp' in self.final_projects: + # Disable OpenMP offload support if not building for NVPTX or AMDGPU + if 'NVPTX' not in self.build_targets and 'AMDGPU' not in self.build_targets: + self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' if not self.cfg['build_openmp_tools']: self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' @@ -355,10 +358,6 @@ def configure_step(self): self._cmakeopts['GCC_INSTALL_PREFIX'] = gcc_prefix self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) - # Disable OpenMP offload support if not building for NVPTX or AMDGPU - if 'NVPTX' not in self.build_targets and 'AMDGPU' not in self.build_targets: - general_opts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' - # If 'NVPTX' is in the build targets we assume the user would like OpenMP offload support as well if 'NVPTX' in self.build_targets: # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): From 0591bbcbac238f8fcffb5c0d3a126d9fb8a0c111 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 12:05:59 +0200 Subject: [PATCH 021/103] Added missing option and authors --- easybuild/easyblocks/l/llvmcore.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 174259771fd..2695fbbb133 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -25,6 +25,10 @@ """ EasyBuild support for building and installing LLVM, implemented as an easyblock +@author: Dmitri Gribenko (National Technical University of Ukraine "KPI") +@author: Ward Poelmans (Ghent University) +@author: Alan O'Cais (Juelich Supercomputing Centre) +@author: Maxime Boissonneault (Digital Research Alliance of Canada, Universite Laval) @author: Simon Branford (University of Birmingham) @author: Kenneth Hoste (Ghent University) @author: Davide Grassano (CECAM HQ - Lausanne) @@ -127,6 +131,7 @@ def extra_options(): 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], + 'default_cuda_capability': [None, "Default CUDA capability specified for clang, e.g. '7.5'", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], }) From 7d84ef5c7f1f4cad9e8a94b28429446065c3c082 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 12:20:09 +0200 Subject: [PATCH 022/103] compiler-rt files --- easybuild/easyblocks/l/llvmcore.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 2695fbbb133..d0403195333 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -720,14 +720,14 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'libmlir_float16_utils.so' ] check_dirs += ['lib/cmake/mlir', 'include/mlir', 'include/mlir-c'] - if 'compiler-rt' in self.final_runtimes: - pth = os.path.join('lib', 'clang', resdir_version, lib_dir_runtime) - # check_files += [os.path.join(pth, _) for _ in [ - # # This should probably be more finetuned depending on what features of compiler-rt are used - # 'libclang_rt.xray.a', 'libclang_rt.fuzzer.a', 'libclang_rt.gwp_asan.a', 'libclang_rt.profile.a', - # 'libclang_rt.lsan.a', 'libclang_rt.asan.a', 'libclang_rt.hwasan.a' - # ]] - # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] + # if 'compiler-rt' in self.final_runtimes: + # pth = os.path.join('lib', 'clang', resdir_version, lib_dir_runtime) + # check_files += [os.path.join(pth, _) for _ in [ + # # This should probably be more finetuned depending on what features of compiler-rt are used + # 'libclang_rt.xray.a', 'libclang_rt.fuzzer.a', 'libclang_rt.gwp_asan.a', 'libclang_rt.profile.a', + # 'libclang_rt.lsan.a', 'libclang_rt.asan.a', 'libclang_rt.hwasan.a' + # ]] + # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] if 'libunwind' in self.final_runtimes: check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.a']] if self.build_shared: From f135f7576a1424f032181fda0576d18c84007cc4 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 14:41:23 +0200 Subject: [PATCH 023/103] Fix for python bindings --- easybuild/easyblocks/l/llvmcore.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index d0403195333..56cd1d84238 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -617,13 +617,13 @@ def post_install_step(self): super(EB_LLVMcore, self).post_install_step() # copy Python bindings here in post-install step so that it is not done more than once in multi_deps context - if self.cfg['python_bindings'] and self.project_name in ['clang', 'mlir']: - python_bindings_source_dir = os.path.join(self.llvm_src_dir, "llvm", "bindings", "python") + if self.cfg['python_bindings']: + python_bindings_source_dir = os.path.join(self.llvm_src_dir, "clang", "bindings", "python") python_bindins_target_dir = os.path.join(self.installdir, 'lib', 'python') shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") - shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) + shutil.copytree(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" @@ -645,13 +645,16 @@ def banned_linked_shared_libs(self): """Return a list of shared libraries that should not be linked against.""" res = [] if self.full_llvm: + self.log.info("Checking that no GCC shared libraries are linked against") res += ['libstdc++', 'libgcc_s', 'libicuuc'] if not self.build_shared: # Libraries should be linked statically + self.log.info("Checking that no shared libraries are linked against in static build") res += ['libc++', 'libc++abi', 'libunwind'] return res def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): + """Perform sanity checks on the installed LLVM.""" lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) shlib_ext = '.' + get_shared_lib_ext() @@ -763,6 +766,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] if self.cfg['python_bindings']: custom_commands += ["python -c 'import clang'"] + custom_commands += ["python -c 'import mlir'"] for libso in filter(lambda x: x.endswith('.so'), check_lib_files): libext = libso.replace('.so', shlib_ext) @@ -779,7 +783,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'dirs': check_dirs, } - return super().sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) + return super(EB_LLVMcore, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) def make_module_extra(self): """Custom variables for Clang module.""" From 9e4cd8280cb1d09325915f04dea1c3c2f08003bf Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 4 Jul 2024 16:44:55 +0200 Subject: [PATCH 024/103] Tested with `build_targets = 'all'` --- easybuild/easyblocks/l/llvmcore.py | 42 +++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 56cd1d84238..10037b67bab 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -47,13 +47,31 @@ mkdir, symlink, which) from easybuild.tools.modules import get_software_root, get_software_version from easybuild.tools.run import run_cmd -from easybuild.tools.systemtools import (get_cpu_architecture, +from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, + X86_64, get_cpu_architecture, get_shared_lib_ext) from easybuild.tools.toolchain.toolchain import Toolchain -from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP from easybuild.easyblocks.generic.cmakemake import CMakeMake +LLVM_TARGETS = [ + 'AArch64', 'AMDGPU', 'ARM', 'AVR', 'BPF', 'Hexagon', 'Lanai', 'LoongArch', 'Mips', 'MSP430', 'NVPTX', 'PowerPC', + 'RISCV', 'Sparc', 'SystemZ', 'VE', 'WebAssembly', 'X86', 'XCore', + 'all' +] +LLVM_EXPERIMENTAL_TARGETS = [ + 'ARC', 'CSKY', 'DirectX', 'M68k', 'SPIRV', 'Xtensa', +] +ALL_TARGETS = LLVM_TARGETS + LLVM_EXPERIMENTAL_TARGETS + +DEFAULT_TARGETS_MAP = { + AARCH32: ['ARM'], + AARCH64: ['AArch64'], + POWER: ['PowerPC'], + RISCV64: ['RISCV'], + X86_64: ['X86'], +} + remove_gcc_dependency_opts = { 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', @@ -116,7 +134,7 @@ def extra_options(): extra_vars.update({ 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + - ', '.join(CLANG_TARGETS), CUSTOM], + ', '.join(ALL_TARGETS), CUSTOM], 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], 'full_llvm': [True, "Build LLVM without any dependency", CUSTOM], 'enable_rtti': [True, "Enable RTTI", CUSTOM], @@ -230,11 +248,15 @@ def __init__(self, *args, **kwargs): except KeyError: raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) - unknown_targets = [target for target in build_targets if target not in CLANG_TARGETS] + unknown_targets = set(build_targets) - set(ALL_TARGETS) if unknown_targets: raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", - ', '.join(unknown_targets), ', '.join(CLANG_TARGETS)) + ', '.join(unknown_targets), ', '.join(ALL_TARGETS)) + exp_targets = set(build_targets) & set(LLVM_EXPERIMENTAL_TARGETS) + if exp_targets: + self.log.warning("Experimental targets %s are being used.", ', '.join(exp_targets)) + self.build_targets = build_targets or [] general_opts['CMAKE_BUILD_TYPE'] = self.build_type @@ -242,7 +264,7 @@ def __init__(self, *args, **kwargs): if self.toolchain.options['pic']: general_opts['CMAKE_POSITION_INDEPENDENT_CODE'] = 'ON' - general_opts['LLVM_TARGETS_TO_BUILD'] = ';'.join(build_targets) + general_opts['LLVM_TARGETS_TO_BUILD'] = '"%s"' % ';'.join(build_targets) self._cmakeopts = {} self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) @@ -280,7 +302,9 @@ def _configure_final_build(self): if 'openmp' in self.final_projects: # Disable OpenMP offload support if not building for NVPTX or AMDGPU - if 'NVPTX' not in self.build_targets and 'AMDGPU' not in self.build_targets: + if any(target in self.build_targets for target in ['NVPTX', 'AMDGPU', 'all']): + self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' + else: self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' if not self.cfg['build_openmp_tools']: @@ -364,7 +388,7 @@ def configure_step(self): self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) # If 'NVPTX' is in the build targets we assume the user would like OpenMP offload support as well - if 'NVPTX' in self.build_targets: + if 'NVPTX' in self.build_targets or 'all' in self.build_targets: # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): # (1) in the easyconfig file, via the custom cuda_compute_capabilities; # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; @@ -390,7 +414,7 @@ def configure_step(self): if not get_software_root('CUDA'): setvar('CUDA_NVCC_EXECUTABLE', 'IGNORE') # If 'AMDGPU' is in the build targets we assume the user would like OpenMP offload support for AMD - if 'AMDGPU' in self.build_targets: + if 'AMDGPU' in self.build_targets or 'all' in self.build_targets: if not get_software_root('ROCR-Runtime'): raise EasyBuildError("Can't build Clang with AMDGPU support " "without dependency 'ROCR-Runtime'") From d3de2bddca5c86b4ad9229e75562bd8d7f44df49 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 5 Jul 2024 11:02:34 +0200 Subject: [PATCH 025/103] Improved test results logging --- easybuild/easyblocks/l/llvmcore.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 10037b67bab..db5bf50e769 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -589,9 +589,11 @@ def _test_step(self, parallel=1): rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) if mch is None: + self.log.info("Failed to extract number of failed test results from output") rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_passed.search(out) if mch is None: + self.log.info("Failed to extract number of passed test results from output") num_failed = None else: num_failed = 0 @@ -601,7 +603,7 @@ def _test_step(self, parallel=1): return num_failed def test_step(self): - """Run Clang tests on final stage (unless disabled).""" + """Run tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: self.log.info("Running test-suite with parallel jobs") num_failed = self._test_step(parallel=self.cfg['parallel']) @@ -614,6 +616,8 @@ def test_step(self): if num_failed > self.cfg['test_suite_max_failed']: raise EasyBuildError("Too many failed tests: %s", num_failed) + self.log.info("Test-suite completed with %s failed tests", num_failed) + def install_step(self): """Install stage 1 or 3 (if bootsrap) binaries.""" basedir = self.final_dir From e9010c1faf038ee05af6ac80d5bdcc16a2ee3292 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 8 Jul 2024 10:36:34 +0200 Subject: [PATCH 026/103] Avoid overriding bramework built-in `_test_step` --- easybuild/easyblocks/l/llvmcore.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index db5bf50e769..11fac8a6cbc 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -405,9 +405,9 @@ def configure_step(self): cuda_cc = [cc.replace('.', '') for cc in cuda_cc] default_cc = default_cc.replace('.', '') general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = 'sm_%s' % default_cc - # OLD (pre v16) flags - general_opts['CLANG_OPENMP_NVPTX_DEFAULT_ARCH'] = 'sm_%s' % default_cc - general_opts['LIBOMPTARGET_NVPTX_COMPUTE_CAPABILITIES'] = ','.join(cuda_cc) + # # OLD (pre v16) flags + # general_opts['CLANG_OPENMP_NVPTX_DEFAULT_ARCH'] = 'sm_%s' % default_cc + # general_opts['LIBOMPTARGET_NVPTX_COMPUTE_CAPABILITIES'] = ','.join(cuda_cc) # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by # using the environment variable which is used as-is and later checked for a falsy value when determining # whether CUDA was found @@ -566,7 +566,7 @@ def build_step(self, verbose=False, path=None): # shutil.rmtree('llvm.obj.3', ignore_errors=True) # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') - def _test_step(self, parallel=1): + def _para_test_step(self, parallel=1): """Run test suite with the specified number of parallel jobs for make.""" basedir = self.final_dir @@ -606,10 +606,10 @@ def test_step(self): """Run tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: self.log.info("Running test-suite with parallel jobs") - num_failed = self._test_step(parallel=self.cfg['parallel']) + num_failed = self._para_test_step(parallel=self.cfg['parallel']) if num_failed is None: self.log.warning("Tests with parallel jobs failed, retrying with single job") - num_failed = self._test_step(parallel=1) + num_failed = self._para_test_step(parallel=1) if num_failed is None: raise EasyBuildError("Failed to extract test results from output") From 2c88aae254b15baf081b3caf67d4b3124ee702b5 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 8 Jul 2024 12:45:29 +0200 Subject: [PATCH 027/103] Allow `libomptarget` for all build_targets --- easybuild/easyblocks/l/llvmcore.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 11fac8a6cbc..cc7e01ba3c1 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -302,10 +302,11 @@ def _configure_final_build(self): if 'openmp' in self.final_projects: # Disable OpenMP offload support if not building for NVPTX or AMDGPU - if any(target in self.build_targets for target in ['NVPTX', 'AMDGPU', 'all']): - self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' - else: - self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' + self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' + # if any(target in self.build_targets for target in ['NVPTX', 'AMDGPU', 'all']): + # self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' + # else: + # self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' if not self.cfg['build_openmp_tools']: self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' From 41d2ae779f4f394cdb6d8b3bcb63b1cd291281e3 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 9 Jul 2024 12:06:38 +0200 Subject: [PATCH 028/103] Improved GPU building and sanity check --- easybuild/easyblocks/l/llvmcore.py | 86 +++++++++++++++++++----------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index cc7e01ba3c1..640d7e4ff3d 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -72,6 +72,12 @@ X86_64: ['X86'], } +AMDGPU_GFX_SUPPORT = [ + 'gfx700', 'gfx701', 'gfx801', 'gfx803', 'gfx900', 'gfx902', 'gfx906', 'gfx908', 'gfx90a', 'gfx90c', + 'gfx940', 'gfx941', 'gfx942', 'gfx1010', 'gfx1030', 'gfx1031', 'gfx1032', 'gfx1033', 'gfx1034', + 'gfx1035', 'gfx1036', 'gfx1100', 'gfx1101', 'gfx1102', 'gfx1103', 'gfx1150', 'gfx1151' +] + remove_gcc_dependency_opts = { 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', @@ -132,6 +138,8 @@ class EB_LLVMcore(CMakeMake): def extra_options(): extra_vars = CMakeMake.extra_options() extra_vars.update({ + 'amd_gfx_list': [None, "List of AMDGPU targets to build for. Possible values: " + + ', '.join(AMDGPU_GFX_SUPPORT), CUSTOM], 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + ', '.join(ALL_TARGETS), CUSTOM], @@ -149,7 +157,7 @@ def extra_options(): 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], - 'default_cuda_capability': [None, "Default CUDA capability specified for clang, e.g. '7.5'", CUSTOM], + # 'default_cuda_capability': [None, "Default CUDA capability specified for clang, e.g. '7.5'", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], }) @@ -388,42 +396,38 @@ def configure_step(self): self._cmakeopts['GCC_INSTALL_PREFIX'] = gcc_prefix self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) - # If 'NVPTX' is in the build targets we assume the user would like OpenMP offload support as well - if 'NVPTX' in self.build_targets or 'all' in self.build_targets: - # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): - # (1) in the easyconfig file, via the custom cuda_compute_capabilities; - # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; - ec_cuda_cc = self.cfg['cuda_compute_capabilities'] - cfg_cuda_cc = build_option('cuda_compute_capabilities') - cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] - if not cuda_cc: - raise EasyBuildError("Can't build Clang with CUDA support " - "without specifying 'cuda-compute-capabilities'") - default_cc = self.cfg['default_cuda_capability'] or min(cuda_cc) - if not self.cfg['default_cuda_capability']: - print_warning("No default CUDA capability defined! " - "Using '%s' taken as minimum from 'cuda_compute_capabilities'" % default_cc) - cuda_cc = [cc.replace('.', '') for cc in cuda_cc] - default_cc = default_cc.replace('.', '') - general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = 'sm_%s' % default_cc - # # OLD (pre v16) flags - # general_opts['CLANG_OPENMP_NVPTX_DEFAULT_ARCH'] = 'sm_%s' % default_cc - # general_opts['LIBOMPTARGET_NVPTX_COMPUTE_CAPABILITIES'] = ','.join(cuda_cc) # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by # using the environment variable which is used as-is and later checked for a falsy value when determining # whether CUDA was found if not get_software_root('CUDA'): setvar('CUDA_NVCC_EXECUTABLE', 'IGNORE') - # If 'AMDGPU' is in the build targets we assume the user would like OpenMP offload support for AMD - if 'AMDGPU' in self.build_targets or 'all' in self.build_targets: - if not get_software_root('ROCR-Runtime'): - raise EasyBuildError("Can't build Clang with AMDGPU support " - "without dependency 'ROCR-Runtime'") - ec_amdgfx = self.cfg['amd_gfx_list'] - if not ec_amdgfx: - raise EasyBuildError("Can't build Clang with AMDGPU support " - "without specifying 'amd_gfx_list'") - general_opts['LIBOMPTARGET_AMDGCN_GFXLIST'] = ' '.join(ec_amdgfx) + + if 'openmp' in self.final_projects: + gpu_archs = [] + # If 'NVPTX' is in the build targets we assume the user would like OpenMP offload support as well + if 'NVPTX' in self.build_targets or 'all' in self.build_targets: + # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): + # (1) in the easyconfig file, via the custom cuda_compute_capabilities; + # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; + ec_cuda_cc = self.cfg['cuda_compute_capabilities'] + cfg_cuda_cc = build_option('cuda_compute_capabilities') + cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] + if not cuda_cc: + raise EasyBuildError("Can't build Clang with CUDA support " + "without specifying 'cuda-compute-capabilities'") + self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] + gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc] + # If 'AMDGPU' is in the build targets we assume the user would like OpenMP offload support for AMD + if 'AMDGPU' in self.build_targets or 'all' in self.build_targets: + if not get_software_root('ROCR-Runtime'): + raise EasyBuildError("Can't build Clang with AMDGPU support " + "without dependency 'ROCR-Runtime'") + self.amd_gfx = self.cfg['amd_gfx_list'] + if not self.amd_gfx: + raise EasyBuildError("Can't build Clang with AMDGPU support " + "without specifying 'amd_gfx_list'") + gpu_archs += self.amd_gfx + general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = ';'.join(gpu_archs) self._configure_general_build() @@ -689,6 +693,17 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F resdir_version = self.version.split('.')[0] + # Detect OpenMP support for CPU architecture + arch = get_cpu_architecture() + # Check architecture explicitly since Clang uses potentially + # different names + if arch == X86_64: + arch = 'x86_64' + elif arch == POWER: + arch = 'ppc64' + elif arch == AARCH64: + arch = 'aarch64' + check_files = [] check_bin_files = [] check_lib_files = [] @@ -791,6 +806,13 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_lib_files += ['libbolt_rt_instr.a'] if 'openmp' in self.final_projects: check_lib_files += ['libomp.so', 'libompd.so'] + check_lib_files += ['libomptarget.%s' % shlib_ext, 'libomptarget.rtl.%s.%s' % (arch, shlib_ext)] + if 'NVPTX' in self.cfg['build_targets']: + check_lib_files += ['libomptarget.rtl.cuda.so'] + check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] + if 'AMDGPU' in self.cfg['build_targets']: + check_lib_files += ['libomptarget.rtl.amdgpu.so'] + check_lib_files += ['llibomptarget-amdgcn-%s.bc' % gfx for gfx in self.amd_gfx] if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] if self.cfg['python_bindings']: From 6f6c06424bfac5ae3741a10d03811319fe735599 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 9 Jul 2024 12:16:04 +0200 Subject: [PATCH 029/103] lint --- easybuild/easyblocks/l/llvmcore.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 640d7e4ff3d..94bb8e2d646 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -414,19 +414,21 @@ def configure_step(self): cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] if not cuda_cc: raise EasyBuildError("Can't build Clang with CUDA support " - "without specifying 'cuda-compute-capabilities'") + "without specifying 'cuda-compute-capabilities'") self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc] + self.log.info("Using CUDA compute capabilities: %s", ', '.join(self.cuda_cc)) # If 'AMDGPU' is in the build targets we assume the user would like OpenMP offload support for AMD if 'AMDGPU' in self.build_targets or 'all' in self.build_targets: if not get_software_root('ROCR-Runtime'): raise EasyBuildError("Can't build Clang with AMDGPU support " - "without dependency 'ROCR-Runtime'") + "without dependency 'ROCR-Runtime'") self.amd_gfx = self.cfg['amd_gfx_list'] if not self.amd_gfx: raise EasyBuildError("Can't build Clang with AMDGPU support " - "without specifying 'amd_gfx_list'") + "without specifying 'amd_gfx_list'") gpu_archs += self.amd_gfx + self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = ';'.join(gpu_archs) self._configure_general_build() From ff78a8dbe7a85ab34fb77a79f4b33a49b26b48fa Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 9 Jul 2024 12:31:19 +0200 Subject: [PATCH 030/103] Fix based on name found in CMakeLists --- easybuild/easyblocks/l/llvmcore.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 94bb8e2d646..a14ad0dfc96 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -429,7 +429,7 @@ def configure_step(self): "without specifying 'amd_gfx_list'") gpu_archs += self.amd_gfx self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) - general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = ';'.join(gpu_archs) + general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) self._configure_general_build() @@ -808,13 +808,13 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_lib_files += ['libbolt_rt_instr.a'] if 'openmp' in self.final_projects: check_lib_files += ['libomp.so', 'libompd.so'] - check_lib_files += ['libomptarget.%s' % shlib_ext, 'libomptarget.rtl.%s.%s' % (arch, shlib_ext)] + check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] if 'NVPTX' in self.cfg['build_targets']: check_lib_files += ['libomptarget.rtl.cuda.so'] check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] if 'AMDGPU' in self.cfg['build_targets']: check_lib_files += ['libomptarget.rtl.amdgpu.so'] - check_lib_files += ['llibomptarget-amdgcn-%s.bc' % gfx for gfx in self.amd_gfx] + check_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] if self.cfg['python_bindings']: From 63cc7842a70e608971f462668b4c98f8130f347e Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 9 Jul 2024 18:03:15 +0200 Subject: [PATCH 031/103] Fix proper handling of GPU targets --- easybuild/easyblocks/l/llvmcore.py | 58 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index a14ad0dfc96..4d04746a092 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -267,6 +267,34 @@ def __init__(self, *args, **kwargs): self.build_targets = build_targets or [] + self.nvptx_cond = 'NVPTX' in self.build_targets + self.amd_cond = 'AMDGPU' in self.build_targets + self.all_cond = 'all' in self.build_targets + self.cuda_cc = [] + if self.nvptx_cond or self.all_cond: + # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): + # (1) in the easyconfig file, via the custom cuda_compute_capabilities; + # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; + ec_cuda_cc = self.cfg['cuda_compute_capabilities'] + cfg_cuda_cc = build_option('cuda_compute_capabilities') + cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] + if not cuda_cc and self.nvptx_cond: + raise EasyBuildError( + "Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'" + ) + else: + self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] + + self.amd_gfx = [] + if self.amd_cond or self.all_cond: + self.amd_gfx = self.cfg['amd_gfx_list'] or [] + if not self.amd_gfx and self.amd_cond: + raise EasyBuildError( + "Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'" + ) + else: + self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) + general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir if self.toolchain.options['pic']: @@ -404,32 +432,10 @@ def configure_step(self): if 'openmp' in self.final_projects: gpu_archs = [] - # If 'NVPTX' is in the build targets we assume the user would like OpenMP offload support as well - if 'NVPTX' in self.build_targets or 'all' in self.build_targets: - # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): - # (1) in the easyconfig file, via the custom cuda_compute_capabilities; - # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; - ec_cuda_cc = self.cfg['cuda_compute_capabilities'] - cfg_cuda_cc = build_option('cuda_compute_capabilities') - cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] - if not cuda_cc: - raise EasyBuildError("Can't build Clang with CUDA support " - "without specifying 'cuda-compute-capabilities'") - self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] - gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc] - self.log.info("Using CUDA compute capabilities: %s", ', '.join(self.cuda_cc)) - # If 'AMDGPU' is in the build targets we assume the user would like OpenMP offload support for AMD - if 'AMDGPU' in self.build_targets or 'all' in self.build_targets: - if not get_software_root('ROCR-Runtime'): - raise EasyBuildError("Can't build Clang with AMDGPU support " - "without dependency 'ROCR-Runtime'") - self.amd_gfx = self.cfg['amd_gfx_list'] - if not self.amd_gfx: - raise EasyBuildError("Can't build Clang with AMDGPU support " - "without specifying 'amd_gfx_list'") - gpu_archs += self.amd_gfx - self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) - general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) + gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc or []] + gpu_archs += self.amd_gfx or [] + if gpu_archs: + general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) self._configure_general_build() From 54834301c4abfd56c682cba815d300bc2d51cefa Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 10 Jul 2024 11:16:19 +0200 Subject: [PATCH 032/103] Made `lld` the default for both `clang` and `flang-new` when enabled --- easybuild/easyblocks/l/llvmcore.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 4d04746a092..bcbfe3d2618 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -98,7 +98,7 @@ 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', 'CLANG_DEFAULT_RTLIB': 'compiler-rt', - 'CLANG_DEFAULT_LINKER': 'lld', + # 'CLANG_DEFAULT_LINKER': 'lld', 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', 'LIBCXX_HAS_GCC_S_LIB': 'Off', @@ -236,6 +236,9 @@ def __init__(self, *args, **kwargs): if self.cfg['build_lld']: self.intermediate_projects.append('lld') self.final_projects.append('lld') + # This should be the default to make offload multi-stage compilations easier + general_opts['CLANG_DEFAULT_LINKER'] = 'lld' + general_opts['FLANG_DEFAULT_LINKER'] = 'lld' if self.cfg['build_lldb']: self.final_projects.append('lldb') if self.full_llvm: From 6c9bb6a3c334e0caa88084c1c5b9edeafaa8d0da Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 10 Jul 2024 14:55:30 +0200 Subject: [PATCH 033/103] Fix and tested RPATH compilation --- easybuild/easyblocks/l/llvmcore.py | 32 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index bcbfe3d2618..de4c8ab2c35 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -39,6 +39,7 @@ import shutil from easybuild.framework.easyconfig import CUSTOM +from easybuild.toolchains.compiler.clang import Clang from easybuild.tools import LooseVersion, run from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning from easybuild.tools.config import build_option @@ -489,11 +490,6 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # orig_library_path = os.getenv('LIBRARY_PATH') orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - self._cmakeopts['CMAKE_C_COMPILER'] = os.path.join(prev_dir, 'bin/clang') - self._cmakeopts['CMAKE_CXX_COMPILER'] = os.path.join(prev_dir, 'bin/clang++') - self._cmakeopts['CMAKE_ASM_COMPILER'] = os.path.join(prev_dir, 'bin/clang') - self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' - self.add_cmake_opts() bin_dir = os.path.join(prev_dir, 'bin') @@ -514,8 +510,14 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 if build_option('rpath'): - my_toolchain = Toolchain(name='llvm', version='1') - my_toolchain.prepare_rpath_wrappers() + my_toolchain = Clang(name='Clang', version='1') + my_toolchain.prepare_rpath_wrappers( + rpath_include_dirs=[ + os.path.join(self.installdir, 'lib'), + os.path.join(self.installdir, 'lib64'), + os.path.join(self.installdir, lib_dir_runtime), + ] + ) self.log.info("Prepared rpath wrappers") # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory @@ -530,11 +532,22 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # resulting in relocation errors). # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100 # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether - cflags = os.getenv('CFLAGS') - cxxflags = os.getenv('CXXFLAGS') + cflags = os.getenv('CFLAGS', '') + cxxflags = os.getenv('CXXFLAGS', '') setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) + # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) + clang = which('clang') + clangxx = which('clang++') + + self._cmakeopts['CMAKE_C_COMPILER'] = clang + self._cmakeopts['CMAKE_CXX_COMPILER'] = clangxx + self._cmakeopts['CMAKE_ASM_COMPILER'] = clang + self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' + + self.add_cmake_opts() + change_dir(stage_dir) self.log.debug("Configuring %s", stage_dir) cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) @@ -558,7 +571,6 @@ def build_step(self, verbose=False, path=None): print_msg("Building stage 1/1") change_dir(self.llvm_obj_dir_stage1) super(EB_LLVMcore, self).build_step(verbose, path) - # import shutil # change_dir(self.builddir) # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") # shutil.rmtree('llvm.obj.1', ignore_errors=True) From adba4c7dabab378a1f40b8a98f93222ea36c4ef1 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 11 Jul 2024 10:35:20 +0200 Subject: [PATCH 034/103] Added context for setting/resetting env vars --- easybuild/easyblocks/l/llvmcore.py | 197 ++++++++++++++--------------- 1 file changed, 95 insertions(+), 102 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index de4c8ab2c35..1a8fd26c072 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -33,6 +33,7 @@ @author: Kenneth Hoste (Ghent University) @author: Davide Grassano (CECAM HQ - Lausanne) """ +import contextlib import glob import os import re @@ -51,7 +52,6 @@ from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, X86_64, get_cpu_architecture, get_shared_lib_ext) -from easybuild.tools.toolchain.toolchain import Toolchain from easybuild.easyblocks.generic.cmakemake import CMakeMake @@ -99,6 +99,7 @@ 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', 'CLANG_DEFAULT_RTLIB': 'compiler-rt', + # Moved to general_opts for ease of building with openmp offload (or other multi-stage builds) # 'CLANG_DEFAULT_LINKER': 'lld', 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', @@ -130,6 +131,25 @@ } +@contextlib.contextmanager +def _wrap_env(path="", ld_path=""): + """Wrap the environment with the path and ld_path.""" + orig_path = os.getenv('PATH', '') + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH', '') + + path = ':'.join(filter(None, [path, orig_path])) + ld_path = ':'.join(filter(None, [ld_path, orig_ld_library_path])) + + setvar('PATH', path) + setvar('LD_LIBRARY_PATH', ld_path) + + try: + yield + finally: + setvar('PATH', orig_path) + setvar('LD_LIBRARY_PATH', orig_ld_library_path) + + class EB_LLVMcore(CMakeMake): """ Support for building and installing LLVM @@ -158,7 +178,6 @@ def extra_options(): 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], - # 'default_cuda_capability': [None, "Default CUDA capability specified for clang, e.g. '7.5'", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], }) @@ -211,7 +230,7 @@ def __init__(self, *args, **kwargs): self.full_llvm = self.cfg['full_llvm'] - # Other vustom options + # Other custom options if self.full_llvm: if not self.cfg['bootstrap']: raise EasyBuildError("Full LLVM build irequires bootstrap build") @@ -319,6 +338,7 @@ def _configure_general_build(self): z3_root = get_software_root("Z3") if z3_root: + self.log.info("Using %s as Z3 root", z3_root) self._cmakeopts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' self._cmakeopts['LLVM_Z3_INSTALL_DIR'] = z3_root @@ -341,12 +361,7 @@ def _configure_final_build(self): self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root if 'openmp' in self.final_projects: - # Disable OpenMP offload support if not building for NVPTX or AMDGPU self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' - # if any(target in self.build_targets for target in ['NVPTX', 'AMDGPU', 'all']): - # self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' - # else: - # self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'OFF' self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' if not self.cfg['build_openmp_tools']: self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' @@ -363,6 +378,16 @@ def configure_step(self): """ Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target """ + gcc_version = get_software_version('GCCcore') + if LooseVersion(gcc_version) < LooseVersion('13'): + raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) + + # Lit is needed for running tests-suite + lit_root = get_software_root('lit') + if not lit_root: + if not self.cfg['skip_all_tests']: + raise EasyBuildError("Can't find `lit`, needed for running tests-suite") + # Parallel build self.make_parallel_opts = "" if self.cfg['parallel']: @@ -381,22 +406,14 @@ def configure_step(self): self.log.info("Initialising for single stage build.") self.final_dir = self.llvm_obj_dir_stage1 + # Libxml2 xml2_root = get_software_root('libxml2') if xml2_root: if self.full_llvm: self.log.warning("LLVM is being built in `full_llvm` mode, libxml2 will not be used") else: - self._cmakeopts['LLVM_ENABLE_LIBXML2'] = 'ON' - # self._cmakeopts['LIBXML2_ROOT'] = xml2_root - - lit_root = get_software_root('lit') - if not lit_root: - if not self.cfg['skip_all_tests']: - raise EasyBuildError("Can't find `lit`, needed for running tests-suite") - - gcc_version = get_software_version('GCCcore') - if LooseVersion(gcc_version) < LooseVersion('13'): - raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) + general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' + # general_opts['LIBXML2_ROOT'] = xml2_root self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: @@ -406,7 +423,6 @@ def configure_step(self): # self.final_projects.append('libc') else: self._configure_final_build() - self.final_dir = self.llvm_obj_dir_stage1 if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: self.log.debug("Disabling the sanitizer tests") @@ -436,8 +452,8 @@ def configure_step(self): if 'openmp' in self.final_projects: gpu_archs = [] - gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc or []] - gpu_archs += self.amd_gfx or [] + gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc] + gpu_archs += self.amd_gfx if gpu_archs: general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) @@ -451,9 +467,6 @@ def configure_step(self): def disable_sanitizer_tests(self): """Disable the tests of all the sanitizers by removing the test directories from the build system""" - - # In Clang 3.6, the sanitizer tests are grouped together in one CMakeLists - # We patch out adding the subdirectories with the sanitizer tests cmakelists_tests = os.path.join(self.llvm_src_dir, 'compiler-rt', 'test', 'CMakeLists.txt') regex_subs = [] regex_subs.append((r'compiler_rt_test_runtime.*san.*', '')) @@ -486,80 +499,69 @@ def configure_step3(self): def build_with_prev_stage(self, prev_dir, stage_dir): """Build LLVM using the previous stage.""" curdir = os.getcwd() - orig_path = os.getenv('PATH') - # orig_library_path = os.getenv('LIBRARY_PATH') - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - - self.add_cmake_opts() bin_dir = os.path.join(prev_dir, 'bin') lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols # e.g. when calling the compiled clang-ast-dump for stage 3 - lib_path = ':'.join([ - # curr_lib_dir, + lib_path = ':'.join(filter(None, [ os.path.join(stage_dir, lib_dir_runtime), - # prev_lib_dir, os.path.join(prev_dir, lib_dir_runtime), - ]) + ])) # Needed for passing the variables to the build command - setvar('PATH', bin_dir + ":" + orig_path) - setvar('LD_LIBRARY_PATH', lib_path + ":" + orig_ld_library_path) - - # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 - if build_option('rpath'): - my_toolchain = Clang(name='Clang', version='1') - my_toolchain.prepare_rpath_wrappers( - rpath_include_dirs=[ - os.path.join(self.installdir, 'lib'), - os.path.join(self.installdir, 'lib64'), - os.path.join(self.installdir, lib_dir_runtime), - ] - ) - self.log.info("Prepared rpath wrappers") - - # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory - # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075 - clang_wrapper_dir = os.path.dirname(which('clang')) - symlink(os.path.join(prev_dir, 'opt'), os.path.join(clang_wrapper_dir, 'opt')) - - # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling - # Clang by default warns about that, and then some configure tests use -Werror which turns those warnings - # into errors. As a result, those configure tests fail, even though the compiler supports the requested - # functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles without - # resulting in relocation errors). - # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100 - # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether - cflags = os.getenv('CFLAGS', '') - cxxflags = os.getenv('CXXFLAGS', '') - setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) - setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) - - # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) - clang = which('clang') - clangxx = which('clang++') - - self._cmakeopts['CMAKE_C_COMPILER'] = clang - self._cmakeopts['CMAKE_CXX_COMPILER'] = clangxx - self._cmakeopts['CMAKE_ASM_COMPILER'] = clang - self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' - - self.add_cmake_opts() - - change_dir(stage_dir) - self.log.debug("Configuring %s", stage_dir) - cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) - run_cmd(cmd, log_all=True) - - self.log.debug("Building %s", stage_dir) - cmd = "make %s VERBOSE=1" % self.make_parallel_opts - run_cmd(cmd, log_all=True) + with _wrap_env(bin_dir, lib_path): + # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 + if build_option('rpath'): + my_toolchain = Clang(name='Clang', version='1') + my_toolchain.prepare_rpath_wrappers( + rpath_include_dirs=[ + os.path.join(self.installdir, 'lib'), + os.path.join(self.installdir, 'lib64'), + os.path.join(self.installdir, lib_dir_runtime), + ] + ) + self.log.info("Prepared rpath wrappers") + + # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory + # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075 + clang_wrapper_dir = os.path.dirname(which('clang')) + symlink(os.path.join(prev_dir, 'opt'), os.path.join(clang_wrapper_dir, 'opt')) + + # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling + # Clang by default warns about that, and then some configure tests use -Werror which turns those + # warnings into errors. As a result, those configure tests fail, even though the compiler supports the + # requested functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles + # without resulting in relocation errors). + # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100 + # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether + cflags = os.getenv('CFLAGS', '') + cxxflags = os.getenv('CXXFLAGS', '') + setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) + setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) + + # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) + clang = which('clang') + clangxx = which('clang++') + + self._cmakeopts['CMAKE_C_COMPILER'] = clang + self._cmakeopts['CMAKE_CXX_COMPILER'] = clangxx + self._cmakeopts['CMAKE_ASM_COMPILER'] = clang + self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' + + self.add_cmake_opts() + + change_dir(stage_dir) + self.log.debug("Configuring %s", stage_dir) + cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) + run_cmd(cmd, log_all=True) + + self.log.debug("Building %s", stage_dir) + cmd = "make %s VERBOSE=1" % self.make_parallel_opts + run_cmd(cmd, log_all=True) change_dir(curdir) - setvar('PATH', orig_path) - setvar('LD_LIBRARY_PATH', orig_ld_library_path) def build_step(self, verbose=False, path=None): """Build LLVM, and optionally build it using itself.""" @@ -599,20 +601,12 @@ def _para_test_step(self, parallel=1): basedir = self.final_dir change_dir(basedir) - orig_path = os.getenv('PATH') - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - # lib_dir = os.path.join(basedir, 'lib') lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - lib_path = ':'.join([os.path.join(basedir, lib_dir_runtime), orig_ld_library_path]) - setvar('PATH', os.path.join(basedir, 'bin') + ":" + orig_path) - setvar('LD_LIBRARY_PATH', lib_path) - - cmd = "make -j %s check-all" % parallel - (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) - self.log.debug(out) - - setvar('PATH', orig_path) - setvar('LD_LIBRARY_PATH', orig_ld_library_path) + lib_path = os.path.join(basedir, lib_dir_runtime) + with _wrap_env(os.path.join(basedir, 'bin'), lib_path): + cmd = "make -j %s check-all" % parallel + (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + self.log.debug(out) rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) @@ -718,8 +712,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F # Detect OpenMP support for CPU architecture arch = get_cpu_architecture() - # Check architecture explicitly since Clang uses potentially - # different names + # Check architecture explicitly since Clang uses potentially different names if arch == X86_64: arch = 'x86_64' elif arch == POWER: From 2396f3b1b8ccbe4ceff4cb93c015e417e8e3168b Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 12 Jul 2024 11:34:30 +0200 Subject: [PATCH 035/103] Apply GCC_INSTALL_PREFIX to all stages --- easybuild/easyblocks/l/llvmcore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index 1a8fd26c072..bfd1c84d37d 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -441,7 +441,7 @@ def configure_step(self): # If that doesn't work either, print error and exit if gcc_prefix is None: raise EasyBuildError("Can't find GCC or GCCcore to use") - self._cmakeopts['GCC_INSTALL_PREFIX'] = gcc_prefix + general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by From db58450be8ef9030fe7b6d487d55edc2434c7b9d Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 17 Jul 2024 14:44:45 +0200 Subject: [PATCH 036/103] Added `minimal` build option --- easybuild/easyblocks/l/llvmcore.py | 93 ++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index bfd1c84d37d..f4da2184342 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -155,6 +155,20 @@ class EB_LLVMcore(CMakeMake): Support for building and installing LLVM """ + minimal_conflicts = [ + 'bootstrap', + 'full_llvm', + 'python_bindings', + 'build_clang_extras', + 'build_bolt', + 'build_lld', + 'build_lldb', + 'build_runtimes', + 'build_openmp', + 'build_openmp_tools', + 'usepolly', + ] + @staticmethod def extra_options(): extra_vars = CMakeMake.extra_options() @@ -166,6 +180,7 @@ def extra_options(): ', '.join(ALL_TARGETS), CUSTOM], 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], 'full_llvm': [True, "Build LLVM without any dependency", CUSTOM], + 'minimal': [False, "Build LLVM only", CUSTOM], 'enable_rtti': [True, "Enable RTTI", CUSTOM], 'skip_all_tests': [False, "Skip running of tests", CUSTOM], 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], @@ -188,7 +203,8 @@ def __init__(self, *args, **kwargs): """Initialize LLVM-specific variables.""" super(EB_LLVMcore, self).__init__(*args, **kwargs) - if LooseVersion(self.version) < LooseVersion('18.1.6'): + # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock + if not self.cfg['minimal'] and LooseVersion(self.version) < LooseVersion('18.1.6'): raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) self.llvm_src_dir = None @@ -197,7 +213,10 @@ def __init__(self, *args, **kwargs): self.llvm_obj_dir_stage3 = None self.intermediate_projects = ['llvm', 'clang'] self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] - self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] + if not self.cfg['minimal']: + self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] + else: + self.final_projects = ['llvm'] self.final_runtimes = [] # Shared @@ -230,6 +249,11 @@ def __init__(self, *args, **kwargs): self.full_llvm = self.cfg['full_llvm'] + if self.cfg['minimal']: + conflicts = [_ for _ in self.minimal_conflicts if self.cfg[_]] + if conflicts: + raise EasyBuildError("Minimal build conflicts with `%s`", ', '.join(conflicts)) + # Other custom options if self.full_llvm: if not self.cfg['bootstrap']: @@ -378,8 +402,9 @@ def configure_step(self): """ Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target """ + # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock gcc_version = get_software_version('GCCcore') - if LooseVersion(gcc_version) < LooseVersion('13'): + if not self.cfg['minimal'] and LooseVersion(gcc_version) < LooseVersion('13'): raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) # Lit is needed for running tests-suite @@ -514,6 +539,8 @@ def build_with_prev_stage(self, prev_dir, stage_dir): with _wrap_env(bin_dir, lib_path): # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 if build_option('rpath'): + # !!! Should be replaced with ClangFlang (or correct naming) toolchain once available + # as this will only create rpath wrappers for Clang and not Flang my_toolchain = Clang(name='Clang', version='1') my_toolchain.prepare_rpath_wrappers( rpath_include_dirs=[ @@ -601,8 +628,10 @@ def _para_test_step(self, parallel=1): basedir = self.final_dir change_dir(basedir) - lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - lib_path = os.path.join(basedir, lib_dir_runtime) + lib_path = '' + if self.cfg['build_runtimes']: + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + lib_path = os.path.join(basedir, lib_dir_runtime) with _wrap_env(os.path.join(basedir, 'bin'), lib_path): cmd = "make -j %s check-all" % parallel (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) @@ -611,11 +640,10 @@ def _para_test_step(self, parallel=1): rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) if mch is None: - self.log.info("Failed to extract number of failed test results from output") rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_passed.search(out) if mch is None: - self.log.info("Failed to extract number of passed test results from output") + self.log.warning("Failed to extract number of failed/passed test results from output") num_failed = None else: num_failed = 0 @@ -627,6 +655,7 @@ def _para_test_step(self, parallel=1): def test_step(self): """Run tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: + max_failed = self.cfg['test_suite_max_failed'] self.log.info("Running test-suite with parallel jobs") num_failed = self._para_test_step(parallel=self.cfg['parallel']) if num_failed is None: @@ -635,30 +664,29 @@ def test_step(self): if num_failed is None: raise EasyBuildError("Failed to extract test results from output") - if num_failed > self.cfg['test_suite_max_failed']: - raise EasyBuildError("Too many failed tests: %s", num_failed) + if num_failed > max_failed: + raise EasyBuildError("Too many failed tests: %s (%s allowed)", num_failed, max_failed) - self.log.info("Test-suite completed with %s failed tests", num_failed) + self.log.info("Test-suite completed with %s failed tests (%s allowed)", num_failed, max_failed) def install_step(self): - """Install stage 1 or 3 (if bootsrap) binaries.""" + """Install stage 1 or 3 (if bootstrap) binaries.""" basedir = self.final_dir change_dir(basedir) - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + if self.cfg['build_runtimes']: + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols - # e.g. when calling the compiled clang-ast-dump for stage 3 - lib_path = ':'.join([ - basedir, - os.path.join(basedir, lib_dir_runtime), - ]) - - # _preinstallopts = self.cfg.get('preinstallopts', '') - self.cfg.update('preinstallopts', ' '.join([ - 'LD_LIBRARY_PATH=%s:%s' % (lib_path, orig_ld_library_path) - ])) + lib_path = ':'.join([ + os.path.join(basedir, lib_dir_runtime), + orig_ld_library_path + ]) + + # _preinstallopts = self.cfg.get('preinstallopts', '') + self.cfg.update('preinstallopts', ' '.join([ + 'LD_LIBRARY_PATH=%s' % lib_path + ])) super(EB_LLVMcore, self).install_step() @@ -705,7 +733,8 @@ def banned_linked_shared_libs(self): def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): """Perform sanity checks on the installed LLVM.""" - lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) + if self.cfg['build_runtimes']: + lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) shlib_ext = '.' + get_shared_lib_ext() resdir_version = self.version.split('.')[0] @@ -727,8 +756,6 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_dirs = ['include/llvm', 'include/llvm-c', 'lib/cmake/llvm'] custom_commands = [ 'llvm-ar --help', 'llvm-ranlib --help', 'llvm-nm --help', 'llvm-objdump --help', - 'llvm-config --cxxflags', 'clang --help', 'clang++ --help', - 'bbc --help', 'mlir-tblgen --help', 'flang-new --help', ] if self.build_shared: @@ -747,6 +774,8 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_dirs += [ 'lib/cmake/clang', 'include/clang' ] + custom_commands += [ 'llvm-config --cxxflags', 'clang --help', 'clang++ --help'] + if 'clang-tools-extra' in self.final_projects: check_bin_files += [ 'clangd', 'clang-tidy', 'clang-pseudo', 'clang-include-fixer', 'clang-query', 'clang-move', @@ -765,6 +794,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'libHLFIRDialect.a' ] check_dirs += ['lib/cmake/flang', 'include/flang'] + custom_commands += ['bbc --help', 'mlir-tblgen --help', 'flang-new --help'] if 'lld' in self.final_projects: check_bin_files += ['ld.lld', 'lld', 'lld-link', 'wasm-ld'] check_lib_files += [ @@ -866,11 +896,14 @@ def make_module_req_guess(self): """ Clang can find its own headers and libraries but the .so's need to be in LD_LIBRARY_PATH """ - runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) + libs = ['lib', 'lib64'] + if self.cfg['build_runtimes']: + runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) + libs.append(runtime_lib_path) guesses = super(EB_LLVMcore, self).make_module_req_guess() guesses.update({ 'CPATH': [], - 'LIBRARY_PATH': ['lib', 'lib64', runtime_lib_path], - 'LD_LIBRARY_PATH': ['lib', 'lib64', runtime_lib_path], + 'LIBRARY_PATH': libs, + 'LD_LIBRARY_PATH': libs, }) return guesses From a83f4cc3c03928680c2b5431ebf30695cf7b57ca Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 17 Jul 2024 14:46:37 +0200 Subject: [PATCH 037/103] lint --- easybuild/easyblocks/l/llvmcore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py index f4da2184342..d8e85a046df 100644 --- a/easybuild/easyblocks/l/llvmcore.py +++ b/easybuild/easyblocks/l/llvmcore.py @@ -774,7 +774,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_dirs += [ 'lib/cmake/clang', 'include/clang' ] - custom_commands += [ 'llvm-config --cxxflags', 'clang --help', 'clang++ --help'] + custom_commands += ['llvm-config --cxxflags', 'clang --help', 'clang++ --help'] if 'clang-tools-extra' in self.final_projects: check_bin_files += [ From d2167ef5f526a2fa5aca62c65f7572cb9de444ba Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 18 Jul 2024 14:01:42 +0200 Subject: [PATCH 038/103] Switched new naming `LLVMcore` to existing `LLVM` --- easybuild/easyblocks/l/llvm.py | 877 ++++++++++++++++++++++++++-- easybuild/easyblocks/l/llvmcore.py | 909 ----------------------------- 2 files changed, 831 insertions(+), 955 deletions(-) delete mode 100644 easybuild/easyblocks/l/llvmcore.py diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 39b98f31846..e9e0d5eab5a 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -25,19 +25,129 @@ """ EasyBuild support for building and installing LLVM, implemented as an easyblock +@author: Dmitri Gribenko (National Technical University of Ukraine "KPI") +@author: Ward Poelmans (Ghent University) +@author: Alan O'Cais (Juelich Supercomputing Centre) +@author: Maxime Boissonneault (Digital Research Alliance of Canada, Universite Laval) @author: Simon Branford (University of Birmingham) @author: Kenneth Hoste (Ghent University) +@author: Davide Grassano (CECAM HQ - Lausanne) """ +import contextlib +import glob import os +import re +import shutil -from easybuild.easyblocks.clang import CLANG_TARGETS, DEFAULT_TARGETS_MAP -from easybuild.easyblocks.generic.cmakemake import CMakeMake from easybuild.framework.easyconfig import CUSTOM -from easybuild.tools.build_log import EasyBuildError -from easybuild.tools.filetools import move_file -from easybuild.tools.modules import get_software_root -from easybuild.tools.systemtools import get_cpu_architecture -from easybuild.tools import LooseVersion +from easybuild.toolchains.compiler.clang import Clang +from easybuild.tools import LooseVersion, run +from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning +from easybuild.tools.config import build_option +from easybuild.tools.environment import setvar +from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, + mkdir, symlink, which) +from easybuild.tools.modules import get_software_root, get_software_version +from easybuild.tools.run import run_cmd +from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, + X86_64, get_cpu_architecture, + get_shared_lib_ext) + +from easybuild.easyblocks.generic.cmakemake import CMakeMake + +LLVM_TARGETS = [ + 'AArch64', 'AMDGPU', 'ARM', 'AVR', 'BPF', 'Hexagon', 'Lanai', 'LoongArch', 'Mips', 'MSP430', 'NVPTX', 'PowerPC', + 'RISCV', 'Sparc', 'SystemZ', 'VE', 'WebAssembly', 'X86', 'XCore', + 'all' +] +LLVM_EXPERIMENTAL_TARGETS = [ + 'ARC', 'CSKY', 'DirectX', 'M68k', 'SPIRV', 'Xtensa', +] +ALL_TARGETS = LLVM_TARGETS + LLVM_EXPERIMENTAL_TARGETS + +DEFAULT_TARGETS_MAP = { + AARCH32: ['ARM'], + AARCH64: ['AArch64'], + POWER: ['PowerPC'], + RISCV64: ['RISCV'], + X86_64: ['X86'], +} + +AMDGPU_GFX_SUPPORT = [ + 'gfx700', 'gfx701', 'gfx801', 'gfx803', 'gfx900', 'gfx902', 'gfx906', 'gfx908', 'gfx90a', 'gfx90c', + 'gfx940', 'gfx941', 'gfx942', 'gfx1010', 'gfx1030', 'gfx1031', 'gfx1032', 'gfx1033', 'gfx1034', + 'gfx1035', 'gfx1036', 'gfx1100', 'gfx1101', 'gfx1102', 'gfx1103', 'gfx1150', 'gfx1151' +] + +remove_gcc_dependency_opts = { + 'LIBCXX_USE_COMPILER_RT': 'On', + 'LIBCXX_CXX_ABI': 'libcxxabi', + 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', + + 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', + 'LIBCXXABI_USE_COMPILER_RT': 'On', + + 'LIBUNWIND_USE_COMPILER_RT': 'On', + + 'SANITIZER_USE_STATIC_LLVM_UNWINDER': 'On', + 'COMPILER_RT_USE_LIBCXX': 'On', + 'COMPILER_RT_USE_LLVM_UNWINDER': 'On', + 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', + 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html + 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', + 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', + + 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', + 'CLANG_DEFAULT_RTLIB': 'compiler-rt', + # Moved to general_opts for ease of building with openmp offload (or other multi-stage builds) + # 'CLANG_DEFAULT_LINKER': 'lld', + 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', + + 'LIBCXX_HAS_GCC_S_LIB': 'Off', + 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', + 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', + + # Libxml2 from system gets autmatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc + 'LLVM_ENABLE_LIBXML2': 'Off', +} + +disable_werror = { + 'LLVM_ENABLE_WERROR': 'Off', + 'BENCHMARK_ENABLE_WERROR': 'Off', + 'COMPILER_RT_ENABLE_WERROR': 'Off', + 'LIBC_WNO_ERROR': 'On', + 'LIBCXX_ENABLE_WERROR': 'Off', + 'LIBUNWIND_ENABLE_WERROR': 'Off', + 'OPENMP_ENABLE_WERROR': 'Off', + 'FLANG_ENABLE_WERROR': 'Off', +} + +general_opts = { + # If EB is launched from a venv, avoid giving priority to the venv's python + 'Python3_FIND_VIRTUALENV': 'STANDARD', + 'LLVM_INSTALL_UTILS': 'ON', + 'LLVM_INCLUDE_BENCHMARKS': 'OFF', + 'CMAKE_VERBOSE_MAKEFILE': 'ON', +} + + +@contextlib.contextmanager +def _wrap_env(path="", ld_path=""): + """Wrap the environment with the path and ld_path.""" + orig_path = os.getenv('PATH', '') + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH', '') + + path = ':'.join(filter(None, [path, orig_path])) + ld_path = ':'.join(filter(None, [ld_path, orig_ld_library_path])) + + setvar('PATH', path) + setvar('LD_LIBRARY_PATH', ld_path) + + try: + yield + finally: + setvar('PATH', orig_path) + setvar('LD_LIBRARY_PATH', orig_ld_library_path) class EB_LLVM(CMakeMake): @@ -45,13 +155,46 @@ class EB_LLVM(CMakeMake): Support for building and installing LLVM """ + minimal_conflicts = [ + 'bootstrap', + 'full_llvm', + 'python_bindings', + 'build_clang_extras', + 'build_bolt', + 'build_lld', + 'build_lldb', + 'build_runtimes', + 'build_openmp', + 'build_openmp_tools', + 'usepolly', + ] + @staticmethod def extra_options(): extra_vars = CMakeMake.extra_options() extra_vars.update({ + 'amd_gfx_list': [None, "List of AMDGPU targets to build for. Possible values: " + + ', '.join(AMDGPU_GFX_SUPPORT), CUSTOM], + 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + - ', '.join(CLANG_TARGETS), CUSTOM], + ', '.join(ALL_TARGETS), CUSTOM], + 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], + 'full_llvm': [True, "Build LLVM without any dependency", CUSTOM], + 'minimal': [False, "Build LLVM only", CUSTOM], 'enable_rtti': [True, "Enable RTTI", CUSTOM], + 'skip_all_tests': [False, "Skip running of tests", CUSTOM], + 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], + 'python_bindings': [False, "Install python bindings", CUSTOM], + 'build_clang_extras': [False, "Build the LLVM Clang extra tools", CUSTOM], + 'build_bolt': [False, "Build the LLVM bolt binary optimizer", CUSTOM], + 'build_lld': [False, "Build the LLVM lld linker", CUSTOM], + 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], + 'build_runtimes': [True, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], + 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], + 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], + 'usepolly': [False, "Build Clang with polly", CUSTOM], + 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], + 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], }) return extra_vars @@ -60,30 +203,96 @@ def __init__(self, *args, **kwargs): """Initialize LLVM-specific variables.""" super(EB_LLVM, self).__init__(*args, **kwargs) + # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock + if not self.cfg['minimal'] and LooseVersion(self.version) < LooseVersion('18.1.6'): + raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) + + self.llvm_src_dir = None + self.llvm_obj_dir_stage1 = None + self.llvm_obj_dir_stage2 = None + self.llvm_obj_dir_stage3 = None + self.intermediate_projects = ['llvm', 'clang'] + self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + if not self.cfg['minimal']: + self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] + else: + self.final_projects = ['llvm'] + self.final_runtimes = [] + + # Shared self.build_shared = self.cfg.get('build_shared_libs', False) - if LooseVersion(self.version) >= LooseVersion('14'): - self.cfg['start_dir'] = '%s-%s.src' % (self.name.lower(), self.version) - # avoid using -DBUILD_SHARED_LIBS directly, use -DLLVM_{BUILD,LINK}_LLVM_DYLIB flags instead - if self.build_shared: - self.cfg['build_shared_libs'] = None + if self.build_shared: + self.cfg['build_shared_libs'] = None + general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'ON' + general_opts['LLVM_LINK_LLVM_DYLIB'] = 'ON' + general_opts['LIBCXX_ENABLE_SHARED'] = 'ON' + general_opts['LIBCXXABI_ENABLE_SHARED'] = 'ON' + general_opts['LIBUNWIND_ENABLE_SHARED'] = 'ON' + else: + general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'OFF' + general_opts['LLVM_LINK_LLVM_DYLIB'] = 'OFF' + general_opts['LIBCXX_ENABLE_SHARED'] = 'OFF' + general_opts['LIBCXXABI_ENABLE_SHARED'] = 'OFF' + general_opts['LIBUNWIND_ENABLE_SHARED'] = 'OFF' + general_opts['LIBCXX_ENABLE_STATIC'] = 'ON' + general_opts['LIBCXX_ENABLE_STATIC_ABI_LIBRARY'] = 'ON' + general_opts['LIBCXX_ENABLE_ABI_LINKER_SCRIPT'] = 'OFF' + general_opts['LIBCXXABI_ENABLE_STATIC'] = 'ON' + general_opts['LIBUNWIND_ENABLE_STATIC'] = 'ON' - def configure_step(self): - """ - Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target - """ - if LooseVersion(self.version) >= LooseVersion('14'): - self.cfg.update('configopts', '-DLLVM_INCLUDE_BENCHMARKS=OFF') - if self.build_shared: - self.cfg.update('configopts', '-DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON') + # RTTI + if self.cfg["enable_rtti"]: + general_opts['LLVM_REQUIRES_RTTI'] = 'ON' + general_opts['LLVM_ENABLE_RTTI'] = 'ON' + # Does not work with Flang + # general_opts['LLVM_ENABLE_EH'] = 'ON' - self.cfg.update('configopts', '-DLLVM_INSTALL_UTILS=ON') + self.full_llvm = self.cfg['full_llvm'] - if get_software_root('zlib'): - self.cfg.update('configopts', '-DLLVM_ENABLE_ZLIB=ON') + if self.cfg['minimal']: + conflicts = [_ for _ in self.minimal_conflicts if self.cfg[_]] + if conflicts: + raise EasyBuildError("Minimal build conflicts with `%s`", ', '.join(conflicts)) - if self.cfg["enable_rtti"]: - self.cfg.update('configopts', '-DLLVM_ENABLE_RTTI=ON') + # Other custom options + if self.full_llvm: + if not self.cfg['bootstrap']: + raise EasyBuildError("Full LLVM build irequires bootstrap build") + if not self.cfg['build_lld']: + raise EasyBuildError("Full LLVM build requires building lld") + if not self.cfg['build_runtimes']: + raise EasyBuildError("Full LLVM build requires building runtimes") + self.log.info("Building LLVM without any GCC dependency") + + if self.cfg['disable_werror']: + general_opts.update(disable_werror) + if self.cfg['build_runtimes']: + self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + if self.cfg['build_openmp']: + self.final_projects.append('openmp') + if self.cfg['build_openmp_tools']: + if not self.cfg['build_openmp']: + raise EasyBuildError("Building OpenMP tools requires building OpenMP runtime") + if self.cfg['usepolly']: + self.final_projects.append('polly') + if self.cfg['build_clang_extras']: + self.final_projects.append('clang-tools-extra') + if self.cfg['build_lld']: + self.intermediate_projects.append('lld') + self.final_projects.append('lld') + # This should be the default to make offload multi-stage compilations easier + general_opts['CLANG_DEFAULT_LINKER'] = 'lld' + general_opts['FLANG_DEFAULT_LINKER'] = 'lld' + if self.cfg['build_lldb']: + self.final_projects.append('lldb') + if self.full_llvm: + remove_gcc_dependency_opts['LLDB_ENABLE_LIBXML2'] = 'Off' + remove_gcc_dependency_opts['LLDB_ENABLE_LZMA'] = 'Off' + remove_gcc_dependency_opts['LLDB_ENABLE_PYTHON'] = 'Off' + if self.cfg['build_bolt']: + self.final_projects.append('bolt') + # Build targets build_targets = self.cfg['build_targets'] if build_targets is None: arch = get_cpu_architecture() @@ -94,31 +303,607 @@ def configure_step(self): except KeyError: raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) - unknown_targets = [target for target in build_targets if target not in CLANG_TARGETS] + unknown_targets = set(build_targets) - set(ALL_TARGETS) if unknown_targets: raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", - ', '.join(unknown_targets), ', '.join(CLANG_TARGETS)) + ', '.join(unknown_targets), ', '.join(ALL_TARGETS)) + exp_targets = set(build_targets) & set(LLVM_EXPERIMENTAL_TARGETS) + if exp_targets: + self.log.warning("Experimental targets %s are being used.", ', '.join(exp_targets)) - self.cfg.update('configopts', '-DLLVM_TARGETS_TO_BUILD="%s"' % ';'.join(build_targets)) + self.build_targets = build_targets or [] + + self.nvptx_cond = 'NVPTX' in self.build_targets + self.amd_cond = 'AMDGPU' in self.build_targets + self.all_cond = 'all' in self.build_targets + self.cuda_cc = [] + if self.nvptx_cond or self.all_cond: + # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): + # (1) in the easyconfig file, via the custom cuda_compute_capabilities; + # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; + ec_cuda_cc = self.cfg['cuda_compute_capabilities'] + cfg_cuda_cc = build_option('cuda_compute_capabilities') + cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] + if not cuda_cc and self.nvptx_cond: + raise EasyBuildError( + "Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'" + ) + else: + self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] - if LooseVersion(self.version) >= LooseVersion('15.0'): - # make sure that CMake modules are available in build directory, - # by moving the extracted folder to the expected location - cmake_modules_path = os.path.join(self.builddir, 'cmake-%s.src' % self.version) - if os.path.exists(cmake_modules_path): - move_file(cmake_modules_path, os.path.join(self.builddir, 'cmake')) + self.amd_gfx = [] + if self.amd_cond or self.all_cond: + self.amd_gfx = self.cfg['amd_gfx_list'] or [] + if not self.amd_gfx and self.amd_cond: + raise EasyBuildError( + "Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'" + ) else: - raise EasyBuildError("Failed to find unpacked CMake modules directory at %s", cmake_modules_path) - - if LooseVersion(self.version) >= LooseVersion('16.0'): - # make sure that third-party modules are available in build directory, - # by moving the extracted folder to the expected location - third_party_modules_path = os.path.join(self.builddir, 'third-party-%s.src' % self.version) - if os.path.exists(third_party_modules_path): - move_file(third_party_modules_path, os.path.join(self.builddir, 'third-party')) + self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) + + general_opts['CMAKE_BUILD_TYPE'] = self.build_type + general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir + if self.toolchain.options['pic']: + general_opts['CMAKE_POSITION_INDEPENDENT_CODE'] = 'ON' + + general_opts['LLVM_TARGETS_TO_BUILD'] = '"%s"' % ';'.join(build_targets) + + self._cmakeopts = {} + self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) + self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) + + def _configure_general_build(self): + """General configuration step for LLVM.""" + self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' + + if get_software_root('zlib'): + self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' + + z3_root = get_software_root("Z3") + if z3_root: + self.log.info("Using %s as Z3 root", z3_root) + self._cmakeopts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' + self._cmakeopts['LLVM_Z3_INSTALL_DIR'] = z3_root + + self._cmakeopts.update(general_opts) + + def _configure_intermediate_build(self): + """Configure the intermediate stages of the build.""" + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.intermediate_runtimes) + + def _configure_final_build(self): + """Configure the final stage of the build.""" + self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.final_projects) + self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.final_runtimes) + + hwloc_root = get_software_root('hwloc') + if hwloc_root: + self.log.info("Using %s as hwloc root", hwloc_root) + self._cmakeopts['LIBOMP_USE_HWLOC'] = 'ON' + self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root + + if 'openmp' in self.final_projects: + self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' + self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' + if not self.cfg['build_openmp_tools']: + self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' + + # Make sure tests are not running with more than `--parallel` tasks + self._cmakeopts['LLVM_LIT_ARGS'] = '"-j %s"' % self.cfg['parallel'] + if self.cfg['usepolly']: + self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' + if not self.cfg['skip_all_tests']: + self._cmakeopts['LLVM_INCLUDE_TESTS'] = 'ON' + self._cmakeopts['LLVM_BUILD_TESTS'] = 'ON' + + def configure_step(self): + """ + Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target + """ + # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock + gcc_version = get_software_version('GCCcore') + if not self.cfg['minimal'] and LooseVersion(gcc_version) < LooseVersion('13'): + raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) + + # Lit is needed for running tests-suite + lit_root = get_software_root('lit') + if not lit_root: + if not self.cfg['skip_all_tests']: + raise EasyBuildError("Can't find `lit`, needed for running tests-suite") + + # Parallel build + self.make_parallel_opts = "" + if self.cfg['parallel']: + self.make_parallel_opts = "-j %s" % self.cfg['parallel'] + + # Bootstrap + self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') + if self.cfg['bootstrap']: + self.log.info("Initialising for bootstrap build.") + self.llvm_obj_dir_stage2 = os.path.join(self.builddir, 'llvm.obj.2') + self.llvm_obj_dir_stage3 = os.path.join(self.builddir, 'llvm.obj.3') + self.final_dir = self.llvm_obj_dir_stage3 + mkdir(self.llvm_obj_dir_stage2) + mkdir(self.llvm_obj_dir_stage3) + else: + self.log.info("Initialising for single stage build.") + self.final_dir = self.llvm_obj_dir_stage1 + + # Libxml2 + xml2_root = get_software_root('libxml2') + if xml2_root: + if self.full_llvm: + self.log.warning("LLVM is being built in `full_llvm` mode, libxml2 will not be used") + else: + general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' + # general_opts['LIBXML2_ROOT'] = xml2_root + + self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') + if self.cfg['bootstrap']: + self._configure_intermediate_build() + # if self.full_llvm: + # self.intermediate_projects.append('libc') + # self.final_projects.append('libc') + else: + self._configure_final_build() + + if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: + self.log.debug("Disabling the sanitizer tests") + self.disable_sanitizer_tests() + + # Remove python bindings tests causing uncaught exception in the build + cmakelists_tests = os.path.join(self.llvm_src_dir, 'clang', 'CMakeLists.txt') + regex_subs = [] + regex_subs.append((r'add_subdirectory\(bindings/python/tests\)', '')) + apply_regex_substitutions(cmakelists_tests, regex_subs) + + gcc_prefix = get_software_root('GCCcore') + # If that doesn't work, try with GCC + if gcc_prefix is None: + gcc_prefix = get_software_root('GCC') + # If that doesn't work either, print error and exit + if gcc_prefix is None: + raise EasyBuildError("Can't find GCC or GCCcore to use") + general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix + self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) + + # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by + # using the environment variable which is used as-is and later checked for a falsy value when determining + # whether CUDA was found + if not get_software_root('CUDA'): + setvar('CUDA_NVCC_EXECUTABLE', 'IGNORE') + + if 'openmp' in self.final_projects: + gpu_archs = [] + gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc] + gpu_archs += self.amd_gfx + if gpu_archs: + general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) + + self._configure_general_build() + + self.add_cmake_opts() + super(EB_LLVM, self).configure_step( + builddir=self.llvm_obj_dir_stage1, + srcdir=os.path.join(self.llvm_src_dir, "llvm") + ) + + def disable_sanitizer_tests(self): + """Disable the tests of all the sanitizers by removing the test directories from the build system""" + cmakelists_tests = os.path.join(self.llvm_src_dir, 'compiler-rt', 'test', 'CMakeLists.txt') + regex_subs = [] + regex_subs.append((r'compiler_rt_test_runtime.*san.*', '')) + + apply_regex_substitutions(cmakelists_tests, regex_subs) + + def add_cmake_opts(self): + """Add LLVM-specific CMake options.""" + base_opts = self._cfgopts.copy() + for k, v in self._cmakeopts.items(): + base_opts.append('-D%s=%s' % (k, v)) + self.cfg['configopts'] = ' '.join(base_opts) + + def configure_step2(self): + """Configure the second stage of the bootstrap.""" + self._cmakeopts = {} + self._configure_general_build() + self._configure_intermediate_build() + if self.full_llvm: + self._cmakeopts.update(remove_gcc_dependency_opts) + + def configure_step3(self): + """Configure the third stage of the bootstrap.""" + self._cmakeopts = {} + self._configure_general_build() + self._configure_final_build() + if self.full_llvm: + self._cmakeopts.update(remove_gcc_dependency_opts) + + def build_with_prev_stage(self, prev_dir, stage_dir): + """Build LLVM using the previous stage.""" + curdir = os.getcwd() + + bin_dir = os.path.join(prev_dir, 'bin') + lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) + + # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols + # e.g. when calling the compiled clang-ast-dump for stage 3 + lib_path = ':'.join(filter(None, [ + os.path.join(stage_dir, lib_dir_runtime), + os.path.join(prev_dir, lib_dir_runtime), + ])) + + # Needed for passing the variables to the build command + with _wrap_env(bin_dir, lib_path): + # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 + if build_option('rpath'): + # !!! Should be replaced with ClangFlang (or correct naming) toolchain once available + # as this will only create rpath wrappers for Clang and not Flang + my_toolchain = Clang(name='Clang', version='1') + my_toolchain.prepare_rpath_wrappers( + rpath_include_dirs=[ + os.path.join(self.installdir, 'lib'), + os.path.join(self.installdir, 'lib64'), + os.path.join(self.installdir, lib_dir_runtime), + ] + ) + self.log.info("Prepared rpath wrappers") + + # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory + # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075 + clang_wrapper_dir = os.path.dirname(which('clang')) + symlink(os.path.join(prev_dir, 'opt'), os.path.join(clang_wrapper_dir, 'opt')) + + # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling + # Clang by default warns about that, and then some configure tests use -Werror which turns those + # warnings into errors. As a result, those configure tests fail, even though the compiler supports the + # requested functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles + # without resulting in relocation errors). + # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100 + # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether + cflags = os.getenv('CFLAGS', '') + cxxflags = os.getenv('CXXFLAGS', '') + setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) + setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) + + # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) + clang = which('clang') + clangxx = which('clang++') + + self._cmakeopts['CMAKE_C_COMPILER'] = clang + self._cmakeopts['CMAKE_CXX_COMPILER'] = clangxx + self._cmakeopts['CMAKE_ASM_COMPILER'] = clang + self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' + + self.add_cmake_opts() + + change_dir(stage_dir) + self.log.debug("Configuring %s", stage_dir) + cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) + run_cmd(cmd, log_all=True) + + self.log.debug("Building %s", stage_dir) + cmd = "make %s VERBOSE=1" % self.make_parallel_opts + run_cmd(cmd, log_all=True) + + change_dir(curdir) + + def build_step(self, verbose=False, path=None): + """Build LLVM, and optionally build it using itself.""" + if self.cfg['bootstrap']: + self.log.info("Building stage 1") + print_msg("Building stage 1/3") + else: + self.log.info("Building LLVM") + print_msg("Building stage 1/1") + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVM, self).build_step(verbose, path) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.1', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + if self.cfg['bootstrap']: + self.log.info("Building stage 2") + print_msg("Building stage 2/3") + self.configure_step2() + self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.2', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + + self.log.info("Building stage 3") + print_msg("Building stage 3/3") + self.configure_step3() + self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.3', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') + + def _para_test_step(self, parallel=1): + """Run test suite with the specified number of parallel jobs for make.""" + basedir = self.final_dir + + change_dir(basedir) + lib_path = '' + if self.cfg['build_runtimes']: + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + lib_path = os.path.join(basedir, lib_dir_runtime) + with _wrap_env(os.path.join(basedir, 'bin'), lib_path): + cmd = "make -j %s check-all" % parallel + (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + self.log.debug(out) + + rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_failed.search(out) + if mch is None: + rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_passed.search(out) + if mch is None: + self.log.warning("Failed to extract number of failed/passed test results from output") + num_failed = None else: - raise EasyBuildError("Failed to find unpacked 'third-party' modules directory at %s", - third_party_modules_path) + num_failed = 0 + else: + num_failed = int(mch.group(1)) + + return num_failed + + def test_step(self): + """Run tests on final stage (unless disabled).""" + if not self.cfg['skip_all_tests']: + max_failed = self.cfg['test_suite_max_failed'] + self.log.info("Running test-suite with parallel jobs") + num_failed = self._para_test_step(parallel=self.cfg['parallel']) + if num_failed is None: + self.log.warning("Tests with parallel jobs failed, retrying with single job") + num_failed = self._para_test_step(parallel=1) + if num_failed is None: + raise EasyBuildError("Failed to extract test results from output") + + if num_failed > max_failed: + raise EasyBuildError("Too many failed tests: %s (%s allowed)", num_failed, max_failed) + + self.log.info("Test-suite completed with %s failed tests (%s allowed)", num_failed, max_failed) + + def install_step(self): + """Install stage 1 or 3 (if bootstrap) binaries.""" + basedir = self.final_dir + change_dir(basedir) - super(EB_LLVM, self).configure_step() + if self.cfg['build_runtimes']: + orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') + lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) + + lib_path = ':'.join([ + os.path.join(basedir, lib_dir_runtime), + orig_ld_library_path + ]) + + # _preinstallopts = self.cfg.get('preinstallopts', '') + self.cfg.update('preinstallopts', ' '.join([ + 'LD_LIBRARY_PATH=%s' % lib_path + ])) + + super(EB_LLVM, self).install_step() + + def post_install_step(self): + """Install python bindings.""" + super(EB_LLVM, self).post_install_step() + + # copy Python bindings here in post-install step so that it is not done more than once in multi_deps context + if self.cfg['python_bindings']: + python_bindings_source_dir = os.path.join(self.llvm_src_dir, "clang", "bindings", "python") + python_bindins_target_dir = os.path.join(self.installdir, 'lib', 'python') + shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) + + python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") + shutil.copytree(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) + + def get_runtime_lib_path(self, base_dir, fail_ok=True): + """Return the path to the runtime libraries.""" + arch = get_cpu_architecture() + glob_pattern = os.path.join(base_dir, 'lib', '%s-*' % arch) + matches = glob.glob(glob_pattern) + if matches: + directory = os.path.basename(matches[0]) + res = os.path.join("lib", directory) + else: + if not fail_ok: + raise EasyBuildError("Could not find runtime library directory") + print_warning("Could not find runtime library directory") + res = "lib" + + return res + + def banned_linked_shared_libs(self): + """Return a list of shared libraries that should not be linked against.""" + res = [] + if self.full_llvm: + self.log.info("Checking that no GCC shared libraries are linked against") + res += ['libstdc++', 'libgcc_s', 'libicuuc'] + if not self.build_shared: + # Libraries should be linked statically + self.log.info("Checking that no shared libraries are linked against in static build") + res += ['libc++', 'libc++abi', 'libunwind'] + return res + + def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): + """Perform sanity checks on the installed LLVM.""" + if self.cfg['build_runtimes']: + lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) + shlib_ext = '.' + get_shared_lib_ext() + + resdir_version = self.version.split('.')[0] + + # Detect OpenMP support for CPU architecture + arch = get_cpu_architecture() + # Check architecture explicitly since Clang uses potentially different names + if arch == X86_64: + arch = 'x86_64' + elif arch == POWER: + arch = 'ppc64' + elif arch == AARCH64: + arch = 'aarch64' + + check_files = [] + check_bin_files = [] + check_lib_files = [] + check_inc_files = [] + check_dirs = ['include/llvm', 'include/llvm-c', 'lib/cmake/llvm'] + custom_commands = [ + 'llvm-ar --help', 'llvm-ranlib --help', 'llvm-nm --help', 'llvm-objdump --help', + ] + + if self.build_shared: + check_lib_files += ['libLLVM.so'] + + if 'clang' in self.final_projects: + check_bin_files += [ + 'clang', 'clang++', 'clang-cpp', 'clang-cl', 'clang-repl', 'hmaptool', 'amdgpu-arch', 'nvptx-arch', + 'intercept-build', 'scan-build', 'scan-build-py', 'scan-view', 'analyze-build', 'c-index-test', + 'clang-tblgen', + ] + check_lib_files += [ + 'libclang.so', 'libclang-cpp.so', 'libclangAST.a', 'libclangCrossTU.a', 'libclangFrontend.a', + 'libclangInterpreter.a', 'libclangParse.a', 'libclangTooling.a' + ] + check_dirs += [ + 'lib/cmake/clang', 'include/clang' + ] + custom_commands += ['llvm-config --cxxflags', 'clang --help', 'clang++ --help'] + + if 'clang-tools-extra' in self.final_projects: + check_bin_files += [ + 'clangd', 'clang-tidy', 'clang-pseudo', 'clang-include-fixer', 'clang-query', 'clang-move', + 'clang-reorder-fields', 'clang-include-cleaner', 'clang-apply-replacements', + 'clang-change-namespace', 'pp-trace', 'modularize' + ] + check_lib_files += [ + 'libclangTidy.a', 'libclangQuery.a', 'libclangIncludeFixer.a', 'libclangIncludeCleaner.a', + ] + check_dirs += ['include/clang-tidy'] + if 'flang' in self.final_projects: + check_bin_files += ['bbc', 'flang-new', 'flang-to-external-fc', 'f18-parse-demo', 'fir-opt', 'tco'] + check_lib_files += [ + 'libFortranRuntime.a', 'libFortranSemantics.a', 'libFortranLower.a', 'libFortranParser.a', + 'libFIRCodeGen.a', 'libflangFrontend.a', 'libFortranCommon.a', 'libFortranDecimal.a', + 'libHLFIRDialect.a' + ] + check_dirs += ['lib/cmake/flang', 'include/flang'] + custom_commands += ['bbc --help', 'mlir-tblgen --help', 'flang-new --help'] + if 'lld' in self.final_projects: + check_bin_files += ['ld.lld', 'lld', 'lld-link', 'wasm-ld'] + check_lib_files += [ + 'liblldCOFF.a', 'liblldCommon.a', 'liblldELF.a', 'liblldMachO.a', 'liblldMinGW.a', 'liblldWasm.a' + ] + check_dirs += ['lib/cmake/lld', 'include/lld'] + if 'lldb' in self.final_projects: + check_bin_files += ['lldb'] + if self.build_shared: + check_lib_files += ['liblldb.so'] + check_dirs += ['include/lldb'] + if 'mlir' in self.final_projects: + check_bin_files += ['mlir-opt', 'tblgen-to-irdl', 'mlir-pdll'] + check_lib_files += [ + 'libMLIRIR.a', 'libmlir_async_runtime.so', 'libmlir_arm_runner_utils.so', 'libmlir_c_runner_utils.so', + 'libmlir_float16_utils.so' + ] + check_dirs += ['lib/cmake/mlir', 'include/mlir', 'include/mlir-c'] + # if 'compiler-rt' in self.final_runtimes: + # pth = os.path.join('lib', 'clang', resdir_version, lib_dir_runtime) + # check_files += [os.path.join(pth, _) for _ in [ + # # This should probably be more finetuned depending on what features of compiler-rt are used + # 'libclang_rt.xray.a', 'libclang_rt.fuzzer.a', 'libclang_rt.gwp_asan.a', 'libclang_rt.profile.a', + # 'libclang_rt.lsan.a', 'libclang_rt.asan.a', 'libclang_rt.hwasan.a' + # ]] + # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] + if 'libunwind' in self.final_runtimes: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.a']] + if self.build_shared: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.so']] + check_inc_files += ['unwind.h', 'libunwind.h', 'mach-o/compact_unwind_encoding.h'] + if 'libcxx' in self.final_runtimes: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.a']] + if self.build_shared: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.so']] + check_dirs += ['include/c++/v1'] + if 'libcxxabi' in self.final_runtimes: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.a']] + if self.build_shared: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.so']] + + if 'polly' in self.final_projects: + check_lib_files += ['libPolly.a', 'libPollyISL.a'] + if self.build_shared: + check_lib_files += ['libPolly.so'] + check_dirs += ['lib/cmake/polly', 'include/polly'] + custom_commands += [ + ' | '.join([ + 'echo \'int main(int argc, char **argv) { return 0; }\'', + 'clang -xc -O3 -mllvm -polly -' + ]) + ' && ./a.out && rm -f a.out' + ] + if 'bolt' in self.final_projects: + check_bin_files += ['llvm-bolt', 'llvm-boltdiff', 'llvm-bolt-heatmap'] + check_lib_files += ['libbolt_rt_instr.a'] + if 'openmp' in self.final_projects: + check_lib_files += ['libomp.so', 'libompd.so'] + check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] + if 'NVPTX' in self.cfg['build_targets']: + check_lib_files += ['libomptarget.rtl.cuda.so'] + check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] + if 'AMDGPU' in self.cfg['build_targets']: + check_lib_files += ['libomptarget.rtl.amdgpu.so'] + check_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] + if self.cfg['build_openmp_tools']: + check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] + if self.cfg['python_bindings']: + custom_commands += ["python -c 'import clang'"] + custom_commands += ["python -c 'import mlir'"] + + for libso in filter(lambda x: x.endswith('.so'), check_lib_files): + libext = libso.replace('.so', shlib_ext) + if libext not in check_lib_files: + check_lib_files.append(libext) + check_lib_files.remove(libso) + + check_files += [os.path.join('bin', _) for _ in check_bin_files] + check_files += [os.path.join('lib', _) for _ in check_lib_files] + check_files += [os.path.join('include', _) for _ in check_inc_files] + + custom_paths = { + 'files': check_files, + 'dirs': check_dirs, + } + + return super(EB_LLVM, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) + + def make_module_extra(self): + """Custom variables for Clang module.""" + txt = super(EB_LLVM, self).make_module_extra() + # we set the symbolizer path so that asan/tsan give meanfull output by default + asan_symbolizer_path = os.path.join(self.installdir, 'bin', 'llvm-symbolizer') + txt += self.module_generator.set_environment('ASAN_SYMBOLIZER_PATH', asan_symbolizer_path) + if self.cfg['python_bindings']: + txt += self.module_generator.prepend_paths('PYTHONPATH', os.path.join("lib", "python")) + return txt + + def make_module_req_guess(self): + """ + Clang can find its own headers and libraries but the .so's need to be in LD_LIBRARY_PATH + """ + libs = ['lib', 'lib64'] + if self.cfg['build_runtimes']: + runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) + libs.append(runtime_lib_path) + guesses = super(EB_LLVM, self).make_module_req_guess() + guesses.update({ + 'CPATH': [], + 'LIBRARY_PATH': libs, + 'LD_LIBRARY_PATH': libs, + }) + return guesses diff --git a/easybuild/easyblocks/l/llvmcore.py b/easybuild/easyblocks/l/llvmcore.py deleted file mode 100644 index d8e85a046df..00000000000 --- a/easybuild/easyblocks/l/llvmcore.py +++ /dev/null @@ -1,909 +0,0 @@ -## -# Copyright 2020-2024 Ghent University -# -# This file is part of EasyBuild, -# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), -# with support of Ghent University (http://ugent.be/hpc), -# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be), -# Flemish Research Foundation (FWO) (http://www.fwo.be/en) -# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). -# -# https://github.com/easybuilders/easybuild -# -# EasyBuild is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation v2. -# -# EasyBuild is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with EasyBuild. If not, see . -## -""" -EasyBuild support for building and installing LLVM, implemented as an easyblock - -@author: Dmitri Gribenko (National Technical University of Ukraine "KPI") -@author: Ward Poelmans (Ghent University) -@author: Alan O'Cais (Juelich Supercomputing Centre) -@author: Maxime Boissonneault (Digital Research Alliance of Canada, Universite Laval) -@author: Simon Branford (University of Birmingham) -@author: Kenneth Hoste (Ghent University) -@author: Davide Grassano (CECAM HQ - Lausanne) -""" -import contextlib -import glob -import os -import re -import shutil - -from easybuild.framework.easyconfig import CUSTOM -from easybuild.toolchains.compiler.clang import Clang -from easybuild.tools import LooseVersion, run -from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning -from easybuild.tools.config import build_option -from easybuild.tools.environment import setvar -from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, - mkdir, symlink, which) -from easybuild.tools.modules import get_software_root, get_software_version -from easybuild.tools.run import run_cmd -from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, - X86_64, get_cpu_architecture, - get_shared_lib_ext) - -from easybuild.easyblocks.generic.cmakemake import CMakeMake - -LLVM_TARGETS = [ - 'AArch64', 'AMDGPU', 'ARM', 'AVR', 'BPF', 'Hexagon', 'Lanai', 'LoongArch', 'Mips', 'MSP430', 'NVPTX', 'PowerPC', - 'RISCV', 'Sparc', 'SystemZ', 'VE', 'WebAssembly', 'X86', 'XCore', - 'all' -] -LLVM_EXPERIMENTAL_TARGETS = [ - 'ARC', 'CSKY', 'DirectX', 'M68k', 'SPIRV', 'Xtensa', -] -ALL_TARGETS = LLVM_TARGETS + LLVM_EXPERIMENTAL_TARGETS - -DEFAULT_TARGETS_MAP = { - AARCH32: ['ARM'], - AARCH64: ['AArch64'], - POWER: ['PowerPC'], - RISCV64: ['RISCV'], - X86_64: ['X86'], -} - -AMDGPU_GFX_SUPPORT = [ - 'gfx700', 'gfx701', 'gfx801', 'gfx803', 'gfx900', 'gfx902', 'gfx906', 'gfx908', 'gfx90a', 'gfx90c', - 'gfx940', 'gfx941', 'gfx942', 'gfx1010', 'gfx1030', 'gfx1031', 'gfx1032', 'gfx1033', 'gfx1034', - 'gfx1035', 'gfx1036', 'gfx1100', 'gfx1101', 'gfx1102', 'gfx1103', 'gfx1150', 'gfx1151' -] - -remove_gcc_dependency_opts = { - 'LIBCXX_USE_COMPILER_RT': 'On', - 'LIBCXX_CXX_ABI': 'libcxxabi', - 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', - - 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', - 'LIBCXXABI_USE_COMPILER_RT': 'On', - - 'LIBUNWIND_USE_COMPILER_RT': 'On', - - 'SANITIZER_USE_STATIC_LLVM_UNWINDER': 'On', - 'COMPILER_RT_USE_LIBCXX': 'On', - 'COMPILER_RT_USE_LLVM_UNWINDER': 'On', - 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', - 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html - 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', - 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', - - 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', - 'CLANG_DEFAULT_RTLIB': 'compiler-rt', - # Moved to general_opts for ease of building with openmp offload (or other multi-stage builds) - # 'CLANG_DEFAULT_LINKER': 'lld', - 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', - - 'LIBCXX_HAS_GCC_S_LIB': 'Off', - 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', - 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', - - # Libxml2 from system gets autmatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc - 'LLVM_ENABLE_LIBXML2': 'Off', -} - -disable_werror = { - 'LLVM_ENABLE_WERROR': 'Off', - 'BENCHMARK_ENABLE_WERROR': 'Off', - 'COMPILER_RT_ENABLE_WERROR': 'Off', - 'LIBC_WNO_ERROR': 'On', - 'LIBCXX_ENABLE_WERROR': 'Off', - 'LIBUNWIND_ENABLE_WERROR': 'Off', - 'OPENMP_ENABLE_WERROR': 'Off', - 'FLANG_ENABLE_WERROR': 'Off', -} - -general_opts = { - # If EB is launched from a venv, avoid giving priority to the venv's python - 'Python3_FIND_VIRTUALENV': 'STANDARD', - 'LLVM_INSTALL_UTILS': 'ON', - 'LLVM_INCLUDE_BENCHMARKS': 'OFF', - 'CMAKE_VERBOSE_MAKEFILE': 'ON', -} - - -@contextlib.contextmanager -def _wrap_env(path="", ld_path=""): - """Wrap the environment with the path and ld_path.""" - orig_path = os.getenv('PATH', '') - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH', '') - - path = ':'.join(filter(None, [path, orig_path])) - ld_path = ':'.join(filter(None, [ld_path, orig_ld_library_path])) - - setvar('PATH', path) - setvar('LD_LIBRARY_PATH', ld_path) - - try: - yield - finally: - setvar('PATH', orig_path) - setvar('LD_LIBRARY_PATH', orig_ld_library_path) - - -class EB_LLVMcore(CMakeMake): - """ - Support for building and installing LLVM - """ - - minimal_conflicts = [ - 'bootstrap', - 'full_llvm', - 'python_bindings', - 'build_clang_extras', - 'build_bolt', - 'build_lld', - 'build_lldb', - 'build_runtimes', - 'build_openmp', - 'build_openmp_tools', - 'usepolly', - ] - - @staticmethod - def extra_options(): - extra_vars = CMakeMake.extra_options() - extra_vars.update({ - 'amd_gfx_list': [None, "List of AMDGPU targets to build for. Possible values: " + - ', '.join(AMDGPU_GFX_SUPPORT), CUSTOM], - 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], - 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + - ', '.join(ALL_TARGETS), CUSTOM], - 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], - 'full_llvm': [True, "Build LLVM without any dependency", CUSTOM], - 'minimal': [False, "Build LLVM only", CUSTOM], - 'enable_rtti': [True, "Enable RTTI", CUSTOM], - 'skip_all_tests': [False, "Skip running of tests", CUSTOM], - 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], - 'python_bindings': [False, "Install python bindings", CUSTOM], - 'build_clang_extras': [False, "Build the LLVM Clang extra tools", CUSTOM], - 'build_bolt': [False, "Build the LLVM bolt binary optimizer", CUSTOM], - 'build_lld': [False, "Build the LLVM lld linker", CUSTOM], - 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], - 'build_runtimes': [True, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], - 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], - 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], - 'usepolly': [False, "Build Clang with polly", CUSTOM], - 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], - 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], - }) - - return extra_vars - - def __init__(self, *args, **kwargs): - """Initialize LLVM-specific variables.""" - super(EB_LLVMcore, self).__init__(*args, **kwargs) - - # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock - if not self.cfg['minimal'] and LooseVersion(self.version) < LooseVersion('18.1.6'): - raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) - - self.llvm_src_dir = None - self.llvm_obj_dir_stage1 = None - self.llvm_obj_dir_stage2 = None - self.llvm_obj_dir_stage3 = None - self.intermediate_projects = ['llvm', 'clang'] - self.intermediate_runtimes = ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] - if not self.cfg['minimal']: - self.final_projects = ['llvm', 'mlir', 'clang', 'flang'] - else: - self.final_projects = ['llvm'] - self.final_runtimes = [] - - # Shared - self.build_shared = self.cfg.get('build_shared_libs', False) - if self.build_shared: - self.cfg['build_shared_libs'] = None - general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'ON' - general_opts['LLVM_LINK_LLVM_DYLIB'] = 'ON' - general_opts['LIBCXX_ENABLE_SHARED'] = 'ON' - general_opts['LIBCXXABI_ENABLE_SHARED'] = 'ON' - general_opts['LIBUNWIND_ENABLE_SHARED'] = 'ON' - else: - general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'OFF' - general_opts['LLVM_LINK_LLVM_DYLIB'] = 'OFF' - general_opts['LIBCXX_ENABLE_SHARED'] = 'OFF' - general_opts['LIBCXXABI_ENABLE_SHARED'] = 'OFF' - general_opts['LIBUNWIND_ENABLE_SHARED'] = 'OFF' - general_opts['LIBCXX_ENABLE_STATIC'] = 'ON' - general_opts['LIBCXX_ENABLE_STATIC_ABI_LIBRARY'] = 'ON' - general_opts['LIBCXX_ENABLE_ABI_LINKER_SCRIPT'] = 'OFF' - general_opts['LIBCXXABI_ENABLE_STATIC'] = 'ON' - general_opts['LIBUNWIND_ENABLE_STATIC'] = 'ON' - - # RTTI - if self.cfg["enable_rtti"]: - general_opts['LLVM_REQUIRES_RTTI'] = 'ON' - general_opts['LLVM_ENABLE_RTTI'] = 'ON' - # Does not work with Flang - # general_opts['LLVM_ENABLE_EH'] = 'ON' - - self.full_llvm = self.cfg['full_llvm'] - - if self.cfg['minimal']: - conflicts = [_ for _ in self.minimal_conflicts if self.cfg[_]] - if conflicts: - raise EasyBuildError("Minimal build conflicts with `%s`", ', '.join(conflicts)) - - # Other custom options - if self.full_llvm: - if not self.cfg['bootstrap']: - raise EasyBuildError("Full LLVM build irequires bootstrap build") - if not self.cfg['build_lld']: - raise EasyBuildError("Full LLVM build requires building lld") - if not self.cfg['build_runtimes']: - raise EasyBuildError("Full LLVM build requires building runtimes") - self.log.info("Building LLVM without any GCC dependency") - - if self.cfg['disable_werror']: - general_opts.update(disable_werror) - if self.cfg['build_runtimes']: - self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] - if self.cfg['build_openmp']: - self.final_projects.append('openmp') - if self.cfg['build_openmp_tools']: - if not self.cfg['build_openmp']: - raise EasyBuildError("Building OpenMP tools requires building OpenMP runtime") - if self.cfg['usepolly']: - self.final_projects.append('polly') - if self.cfg['build_clang_extras']: - self.final_projects.append('clang-tools-extra') - if self.cfg['build_lld']: - self.intermediate_projects.append('lld') - self.final_projects.append('lld') - # This should be the default to make offload multi-stage compilations easier - general_opts['CLANG_DEFAULT_LINKER'] = 'lld' - general_opts['FLANG_DEFAULT_LINKER'] = 'lld' - if self.cfg['build_lldb']: - self.final_projects.append('lldb') - if self.full_llvm: - remove_gcc_dependency_opts['LLDB_ENABLE_LIBXML2'] = 'Off' - remove_gcc_dependency_opts['LLDB_ENABLE_LZMA'] = 'Off' - remove_gcc_dependency_opts['LLDB_ENABLE_PYTHON'] = 'Off' - if self.cfg['build_bolt']: - self.final_projects.append('bolt') - - # Build targets - build_targets = self.cfg['build_targets'] - if build_targets is None: - arch = get_cpu_architecture() - try: - default_targets = DEFAULT_TARGETS_MAP[arch][:] - self.cfg['build_targets'] = build_targets = default_targets - self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) - except KeyError: - raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) - - unknown_targets = set(build_targets) - set(ALL_TARGETS) - - if unknown_targets: - raise EasyBuildError("Some of the chosen build targets (%s) are not in %s.", - ', '.join(unknown_targets), ', '.join(ALL_TARGETS)) - exp_targets = set(build_targets) & set(LLVM_EXPERIMENTAL_TARGETS) - if exp_targets: - self.log.warning("Experimental targets %s are being used.", ', '.join(exp_targets)) - - self.build_targets = build_targets or [] - - self.nvptx_cond = 'NVPTX' in self.build_targets - self.amd_cond = 'AMDGPU' in self.build_targets - self.all_cond = 'all' in self.build_targets - self.cuda_cc = [] - if self.nvptx_cond or self.all_cond: - # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): - # (1) in the easyconfig file, via the custom cuda_compute_capabilities; - # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; - ec_cuda_cc = self.cfg['cuda_compute_capabilities'] - cfg_cuda_cc = build_option('cuda_compute_capabilities') - cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] - if not cuda_cc and self.nvptx_cond: - raise EasyBuildError( - "Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'" - ) - else: - self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] - - self.amd_gfx = [] - if self.amd_cond or self.all_cond: - self.amd_gfx = self.cfg['amd_gfx_list'] or [] - if not self.amd_gfx and self.amd_cond: - raise EasyBuildError( - "Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'" - ) - else: - self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) - - general_opts['CMAKE_BUILD_TYPE'] = self.build_type - general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir - if self.toolchain.options['pic']: - general_opts['CMAKE_POSITION_INDEPENDENT_CODE'] = 'ON' - - general_opts['LLVM_TARGETS_TO_BUILD'] = '"%s"' % ';'.join(build_targets) - - self._cmakeopts = {} - self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) - self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) - - def _configure_general_build(self): - """General configuration step for LLVM.""" - self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' - - if get_software_root('zlib'): - self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' - - z3_root = get_software_root("Z3") - if z3_root: - self.log.info("Using %s as Z3 root", z3_root) - self._cmakeopts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' - self._cmakeopts['LLVM_Z3_INSTALL_DIR'] = z3_root - - self._cmakeopts.update(general_opts) - - def _configure_intermediate_build(self): - """Configure the intermediate stages of the build.""" - self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) - self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.intermediate_runtimes) - - def _configure_final_build(self): - """Configure the final stage of the build.""" - self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.final_projects) - self._cmakeopts['LLVM_ENABLE_RUNTIMES'] = '"%s"' % ';'.join(self.final_runtimes) - - hwloc_root = get_software_root('hwloc') - if hwloc_root: - self.log.info("Using %s as hwloc root", hwloc_root) - self._cmakeopts['LIBOMP_USE_HWLOC'] = 'ON' - self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root - - if 'openmp' in self.final_projects: - self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' - self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' - if not self.cfg['build_openmp_tools']: - self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' - - # Make sure tests are not running with more than `--parallel` tasks - self._cmakeopts['LLVM_LIT_ARGS'] = '"-j %s"' % self.cfg['parallel'] - if self.cfg['usepolly']: - self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' - if not self.cfg['skip_all_tests']: - self._cmakeopts['LLVM_INCLUDE_TESTS'] = 'ON' - self._cmakeopts['LLVM_BUILD_TESTS'] = 'ON' - - def configure_step(self): - """ - Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target - """ - # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock - gcc_version = get_software_version('GCCcore') - if not self.cfg['minimal'] and LooseVersion(gcc_version) < LooseVersion('13'): - raise EasyBuildError("LLVM %s requires GCC 13 or newer, found %s", self.version, gcc_version) - - # Lit is needed for running tests-suite - lit_root = get_software_root('lit') - if not lit_root: - if not self.cfg['skip_all_tests']: - raise EasyBuildError("Can't find `lit`, needed for running tests-suite") - - # Parallel build - self.make_parallel_opts = "" - if self.cfg['parallel']: - self.make_parallel_opts = "-j %s" % self.cfg['parallel'] - - # Bootstrap - self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') - if self.cfg['bootstrap']: - self.log.info("Initialising for bootstrap build.") - self.llvm_obj_dir_stage2 = os.path.join(self.builddir, 'llvm.obj.2') - self.llvm_obj_dir_stage3 = os.path.join(self.builddir, 'llvm.obj.3') - self.final_dir = self.llvm_obj_dir_stage3 - mkdir(self.llvm_obj_dir_stage2) - mkdir(self.llvm_obj_dir_stage3) - else: - self.log.info("Initialising for single stage build.") - self.final_dir = self.llvm_obj_dir_stage1 - - # Libxml2 - xml2_root = get_software_root('libxml2') - if xml2_root: - if self.full_llvm: - self.log.warning("LLVM is being built in `full_llvm` mode, libxml2 will not be used") - else: - general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' - # general_opts['LIBXML2_ROOT'] = xml2_root - - self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') - if self.cfg['bootstrap']: - self._configure_intermediate_build() - # if self.full_llvm: - # self.intermediate_projects.append('libc') - # self.final_projects.append('libc') - else: - self._configure_final_build() - - if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: - self.log.debug("Disabling the sanitizer tests") - self.disable_sanitizer_tests() - - # Remove python bindings tests causing uncaught exception in the build - cmakelists_tests = os.path.join(self.llvm_src_dir, 'clang', 'CMakeLists.txt') - regex_subs = [] - regex_subs.append((r'add_subdirectory\(bindings/python/tests\)', '')) - apply_regex_substitutions(cmakelists_tests, regex_subs) - - gcc_prefix = get_software_root('GCCcore') - # If that doesn't work, try with GCC - if gcc_prefix is None: - gcc_prefix = get_software_root('GCC') - # If that doesn't work either, print error and exit - if gcc_prefix is None: - raise EasyBuildError("Can't find GCC or GCCcore to use") - general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix - self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) - - # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by - # using the environment variable which is used as-is and later checked for a falsy value when determining - # whether CUDA was found - if not get_software_root('CUDA'): - setvar('CUDA_NVCC_EXECUTABLE', 'IGNORE') - - if 'openmp' in self.final_projects: - gpu_archs = [] - gpu_archs += ['sm_%s' % cc for cc in self.cuda_cc] - gpu_archs += self.amd_gfx - if gpu_archs: - general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) - - self._configure_general_build() - - self.add_cmake_opts() - super(EB_LLVMcore, self).configure_step( - builddir=self.llvm_obj_dir_stage1, - srcdir=os.path.join(self.llvm_src_dir, "llvm") - ) - - def disable_sanitizer_tests(self): - """Disable the tests of all the sanitizers by removing the test directories from the build system""" - cmakelists_tests = os.path.join(self.llvm_src_dir, 'compiler-rt', 'test', 'CMakeLists.txt') - regex_subs = [] - regex_subs.append((r'compiler_rt_test_runtime.*san.*', '')) - - apply_regex_substitutions(cmakelists_tests, regex_subs) - - def add_cmake_opts(self): - """Add LLVM-specific CMake options.""" - base_opts = self._cfgopts.copy() - for k, v in self._cmakeopts.items(): - base_opts.append('-D%s=%s' % (k, v)) - self.cfg['configopts'] = ' '.join(base_opts) - - def configure_step2(self): - """Configure the second stage of the bootstrap.""" - self._cmakeopts = {} - self._configure_general_build() - self._configure_intermediate_build() - if self.full_llvm: - self._cmakeopts.update(remove_gcc_dependency_opts) - - def configure_step3(self): - """Configure the third stage of the bootstrap.""" - self._cmakeopts = {} - self._configure_general_build() - self._configure_final_build() - if self.full_llvm: - self._cmakeopts.update(remove_gcc_dependency_opts) - - def build_with_prev_stage(self, prev_dir, stage_dir): - """Build LLVM using the previous stage.""" - curdir = os.getcwd() - - bin_dir = os.path.join(prev_dir, 'bin') - lib_dir_runtime = self.get_runtime_lib_path(prev_dir, fail_ok=False) - - # Give priority to the libraries in the current stage if compiled to avoid failures due to undefined symbols - # e.g. when calling the compiled clang-ast-dump for stage 3 - lib_path = ':'.join(filter(None, [ - os.path.join(stage_dir, lib_dir_runtime), - os.path.join(prev_dir, lib_dir_runtime), - ])) - - # Needed for passing the variables to the build command - with _wrap_env(bin_dir, lib_path): - # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 - if build_option('rpath'): - # !!! Should be replaced with ClangFlang (or correct naming) toolchain once available - # as this will only create rpath wrappers for Clang and not Flang - my_toolchain = Clang(name='Clang', version='1') - my_toolchain.prepare_rpath_wrappers( - rpath_include_dirs=[ - os.path.join(self.installdir, 'lib'), - os.path.join(self.installdir, 'lib64'), - os.path.join(self.installdir, lib_dir_runtime), - ] - ) - self.log.info("Prepared rpath wrappers") - - # add symlink for 'opt' to wrapper dir, since Clang expects it in the same directory - # see https://github.com/easybuilders/easybuild-easyblocks/issues/3075 - clang_wrapper_dir = os.path.dirname(which('clang')) - symlink(os.path.join(prev_dir, 'opt'), os.path.join(clang_wrapper_dir, 'opt')) - - # RPATH wrappers add -Wl,rpath arguments to all command lines, including when it is just compiling - # Clang by default warns about that, and then some configure tests use -Werror which turns those - # warnings into errors. As a result, those configure tests fail, even though the compiler supports the - # requested functionality (e.g. the test that checks if -fPIC is supported would fail, and it compiles - # without resulting in relocation errors). - # See https://github.com/easybuilders/easybuild-easyblocks/pull/2799#issuecomment-1270621100 - # Here, we add -Wno-unused-command-line-argument to CXXFLAGS to avoid these warnings alltogether - cflags = os.getenv('CFLAGS', '') - cxxflags = os.getenv('CXXFLAGS', '') - setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) - setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) - - # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) - clang = which('clang') - clangxx = which('clang++') - - self._cmakeopts['CMAKE_C_COMPILER'] = clang - self._cmakeopts['CMAKE_CXX_COMPILER'] = clangxx - self._cmakeopts['CMAKE_ASM_COMPILER'] = clang - self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' - - self.add_cmake_opts() - - change_dir(stage_dir) - self.log.debug("Configuring %s", stage_dir) - cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) - run_cmd(cmd, log_all=True) - - self.log.debug("Building %s", stage_dir) - cmd = "make %s VERBOSE=1" % self.make_parallel_opts - run_cmd(cmd, log_all=True) - - change_dir(curdir) - - def build_step(self, verbose=False, path=None): - """Build LLVM, and optionally build it using itself.""" - if self.cfg['bootstrap']: - self.log.info("Building stage 1") - print_msg("Building stage 1/3") - else: - self.log.info("Building LLVM") - print_msg("Building stage 1/1") - change_dir(self.llvm_obj_dir_stage1) - super(EB_LLVMcore, self).build_step(verbose, path) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.1', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') - if self.cfg['bootstrap']: - self.log.info("Building stage 2") - print_msg("Building stage 2/3") - self.configure_step2() - self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.2', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') - - self.log.info("Building stage 3") - print_msg("Building stage 3/3") - self.configure_step3() - self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.3', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') - - def _para_test_step(self, parallel=1): - """Run test suite with the specified number of parallel jobs for make.""" - basedir = self.final_dir - - change_dir(basedir) - lib_path = '' - if self.cfg['build_runtimes']: - lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - lib_path = os.path.join(basedir, lib_dir_runtime) - with _wrap_env(os.path.join(basedir, 'bin'), lib_path): - cmd = "make -j %s check-all" % parallel - (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) - self.log.debug(out) - - rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) - mch = rgx_failed.search(out) - if mch is None: - rgx_passed = re.compile(r'^ +Passed +: +([0-9]+)', flags=re.MULTILINE) - mch = rgx_passed.search(out) - if mch is None: - self.log.warning("Failed to extract number of failed/passed test results from output") - num_failed = None - else: - num_failed = 0 - else: - num_failed = int(mch.group(1)) - - return num_failed - - def test_step(self): - """Run tests on final stage (unless disabled).""" - if not self.cfg['skip_all_tests']: - max_failed = self.cfg['test_suite_max_failed'] - self.log.info("Running test-suite with parallel jobs") - num_failed = self._para_test_step(parallel=self.cfg['parallel']) - if num_failed is None: - self.log.warning("Tests with parallel jobs failed, retrying with single job") - num_failed = self._para_test_step(parallel=1) - if num_failed is None: - raise EasyBuildError("Failed to extract test results from output") - - if num_failed > max_failed: - raise EasyBuildError("Too many failed tests: %s (%s allowed)", num_failed, max_failed) - - self.log.info("Test-suite completed with %s failed tests (%s allowed)", num_failed, max_failed) - - def install_step(self): - """Install stage 1 or 3 (if bootstrap) binaries.""" - basedir = self.final_dir - change_dir(basedir) - - if self.cfg['build_runtimes']: - orig_ld_library_path = os.getenv('LD_LIBRARY_PATH') - lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) - - lib_path = ':'.join([ - os.path.join(basedir, lib_dir_runtime), - orig_ld_library_path - ]) - - # _preinstallopts = self.cfg.get('preinstallopts', '') - self.cfg.update('preinstallopts', ' '.join([ - 'LD_LIBRARY_PATH=%s' % lib_path - ])) - - super(EB_LLVMcore, self).install_step() - - def post_install_step(self): - """Install python bindings.""" - super(EB_LLVMcore, self).post_install_step() - - # copy Python bindings here in post-install step so that it is not done more than once in multi_deps context - if self.cfg['python_bindings']: - python_bindings_source_dir = os.path.join(self.llvm_src_dir, "clang", "bindings", "python") - python_bindins_target_dir = os.path.join(self.installdir, 'lib', 'python') - shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) - - python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") - shutil.copytree(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) - - def get_runtime_lib_path(self, base_dir, fail_ok=True): - """Return the path to the runtime libraries.""" - arch = get_cpu_architecture() - glob_pattern = os.path.join(base_dir, 'lib', '%s-*' % arch) - matches = glob.glob(glob_pattern) - if matches: - directory = os.path.basename(matches[0]) - res = os.path.join("lib", directory) - else: - if not fail_ok: - raise EasyBuildError("Could not find runtime library directory") - print_warning("Could not find runtime library directory") - res = "lib" - - return res - - def banned_linked_shared_libs(self): - """Return a list of shared libraries that should not be linked against.""" - res = [] - if self.full_llvm: - self.log.info("Checking that no GCC shared libraries are linked against") - res += ['libstdc++', 'libgcc_s', 'libicuuc'] - if not self.build_shared: - # Libraries should be linked statically - self.log.info("Checking that no shared libraries are linked against in static build") - res += ['libc++', 'libc++abi', 'libunwind'] - return res - - def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): - """Perform sanity checks on the installed LLVM.""" - if self.cfg['build_runtimes']: - lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) - shlib_ext = '.' + get_shared_lib_ext() - - resdir_version = self.version.split('.')[0] - - # Detect OpenMP support for CPU architecture - arch = get_cpu_architecture() - # Check architecture explicitly since Clang uses potentially different names - if arch == X86_64: - arch = 'x86_64' - elif arch == POWER: - arch = 'ppc64' - elif arch == AARCH64: - arch = 'aarch64' - - check_files = [] - check_bin_files = [] - check_lib_files = [] - check_inc_files = [] - check_dirs = ['include/llvm', 'include/llvm-c', 'lib/cmake/llvm'] - custom_commands = [ - 'llvm-ar --help', 'llvm-ranlib --help', 'llvm-nm --help', 'llvm-objdump --help', - ] - - if self.build_shared: - check_lib_files += ['libLLVM.so'] - - if 'clang' in self.final_projects: - check_bin_files += [ - 'clang', 'clang++', 'clang-cpp', 'clang-cl', 'clang-repl', 'hmaptool', 'amdgpu-arch', 'nvptx-arch', - 'intercept-build', 'scan-build', 'scan-build-py', 'scan-view', 'analyze-build', 'c-index-test', - 'clang-tblgen', - ] - check_lib_files += [ - 'libclang.so', 'libclang-cpp.so', 'libclangAST.a', 'libclangCrossTU.a', 'libclangFrontend.a', - 'libclangInterpreter.a', 'libclangParse.a', 'libclangTooling.a' - ] - check_dirs += [ - 'lib/cmake/clang', 'include/clang' - ] - custom_commands += ['llvm-config --cxxflags', 'clang --help', 'clang++ --help'] - - if 'clang-tools-extra' in self.final_projects: - check_bin_files += [ - 'clangd', 'clang-tidy', 'clang-pseudo', 'clang-include-fixer', 'clang-query', 'clang-move', - 'clang-reorder-fields', 'clang-include-cleaner', 'clang-apply-replacements', - 'clang-change-namespace', 'pp-trace', 'modularize' - ] - check_lib_files += [ - 'libclangTidy.a', 'libclangQuery.a', 'libclangIncludeFixer.a', 'libclangIncludeCleaner.a', - ] - check_dirs += ['include/clang-tidy'] - if 'flang' in self.final_projects: - check_bin_files += ['bbc', 'flang-new', 'flang-to-external-fc', 'f18-parse-demo', 'fir-opt', 'tco'] - check_lib_files += [ - 'libFortranRuntime.a', 'libFortranSemantics.a', 'libFortranLower.a', 'libFortranParser.a', - 'libFIRCodeGen.a', 'libflangFrontend.a', 'libFortranCommon.a', 'libFortranDecimal.a', - 'libHLFIRDialect.a' - ] - check_dirs += ['lib/cmake/flang', 'include/flang'] - custom_commands += ['bbc --help', 'mlir-tblgen --help', 'flang-new --help'] - if 'lld' in self.final_projects: - check_bin_files += ['ld.lld', 'lld', 'lld-link', 'wasm-ld'] - check_lib_files += [ - 'liblldCOFF.a', 'liblldCommon.a', 'liblldELF.a', 'liblldMachO.a', 'liblldMinGW.a', 'liblldWasm.a' - ] - check_dirs += ['lib/cmake/lld', 'include/lld'] - if 'lldb' in self.final_projects: - check_bin_files += ['lldb'] - if self.build_shared: - check_lib_files += ['liblldb.so'] - check_dirs += ['include/lldb'] - if 'mlir' in self.final_projects: - check_bin_files += ['mlir-opt', 'tblgen-to-irdl', 'mlir-pdll'] - check_lib_files += [ - 'libMLIRIR.a', 'libmlir_async_runtime.so', 'libmlir_arm_runner_utils.so', 'libmlir_c_runner_utils.so', - 'libmlir_float16_utils.so' - ] - check_dirs += ['lib/cmake/mlir', 'include/mlir', 'include/mlir-c'] - # if 'compiler-rt' in self.final_runtimes: - # pth = os.path.join('lib', 'clang', resdir_version, lib_dir_runtime) - # check_files += [os.path.join(pth, _) for _ in [ - # # This should probably be more finetuned depending on what features of compiler-rt are used - # 'libclang_rt.xray.a', 'libclang_rt.fuzzer.a', 'libclang_rt.gwp_asan.a', 'libclang_rt.profile.a', - # 'libclang_rt.lsan.a', 'libclang_rt.asan.a', 'libclang_rt.hwasan.a' - # ]] - # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] - if 'libunwind' in self.final_runtimes: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.a']] - if self.build_shared: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.so']] - check_inc_files += ['unwind.h', 'libunwind.h', 'mach-o/compact_unwind_encoding.h'] - if 'libcxx' in self.final_runtimes: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.a']] - if self.build_shared: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.so']] - check_dirs += ['include/c++/v1'] - if 'libcxxabi' in self.final_runtimes: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.a']] - if self.build_shared: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.so']] - - if 'polly' in self.final_projects: - check_lib_files += ['libPolly.a', 'libPollyISL.a'] - if self.build_shared: - check_lib_files += ['libPolly.so'] - check_dirs += ['lib/cmake/polly', 'include/polly'] - custom_commands += [ - ' | '.join([ - 'echo \'int main(int argc, char **argv) { return 0; }\'', - 'clang -xc -O3 -mllvm -polly -' - ]) + ' && ./a.out && rm -f a.out' - ] - if 'bolt' in self.final_projects: - check_bin_files += ['llvm-bolt', 'llvm-boltdiff', 'llvm-bolt-heatmap'] - check_lib_files += ['libbolt_rt_instr.a'] - if 'openmp' in self.final_projects: - check_lib_files += ['libomp.so', 'libompd.so'] - check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] - if 'NVPTX' in self.cfg['build_targets']: - check_lib_files += ['libomptarget.rtl.cuda.so'] - check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] - if 'AMDGPU' in self.cfg['build_targets']: - check_lib_files += ['libomptarget.rtl.amdgpu.so'] - check_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] - if self.cfg['build_openmp_tools']: - check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] - if self.cfg['python_bindings']: - custom_commands += ["python -c 'import clang'"] - custom_commands += ["python -c 'import mlir'"] - - for libso in filter(lambda x: x.endswith('.so'), check_lib_files): - libext = libso.replace('.so', shlib_ext) - if libext not in check_lib_files: - check_lib_files.append(libext) - check_lib_files.remove(libso) - - check_files += [os.path.join('bin', _) for _ in check_bin_files] - check_files += [os.path.join('lib', _) for _ in check_lib_files] - check_files += [os.path.join('include', _) for _ in check_inc_files] - - custom_paths = { - 'files': check_files, - 'dirs': check_dirs, - } - - return super(EB_LLVMcore, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) - - def make_module_extra(self): - """Custom variables for Clang module.""" - txt = super(EB_LLVMcore, self).make_module_extra() - # we set the symbolizer path so that asan/tsan give meanfull output by default - asan_symbolizer_path = os.path.join(self.installdir, 'bin', 'llvm-symbolizer') - txt += self.module_generator.set_environment('ASAN_SYMBOLIZER_PATH', asan_symbolizer_path) - if self.cfg['python_bindings']: - txt += self.module_generator.prepend_paths('PYTHONPATH', os.path.join("lib", "python")) - return txt - - def make_module_req_guess(self): - """ - Clang can find its own headers and libraries but the .so's need to be in LD_LIBRARY_PATH - """ - libs = ['lib', 'lib64'] - if self.cfg['build_runtimes']: - runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) - libs.append(runtime_lib_path) - guesses = super(EB_LLVMcore, self).make_module_req_guess() - guesses.update({ - 'CPATH': [], - 'LIBRARY_PATH': libs, - 'LD_LIBRARY_PATH': libs, - }) - return guesses From 879ced7ed80ba59465198f75b4eede894e1b580d Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 18 Jul 2024 14:36:52 +0200 Subject: [PATCH 039/103] Switched full_llvm default --- easybuild/easyblocks/l/llvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index e9e0d5eab5a..66988105374 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -179,7 +179,7 @@ def extra_options(): 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + ', '.join(ALL_TARGETS), CUSTOM], 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], - 'full_llvm': [True, "Build LLVM without any dependency", CUSTOM], + 'full_llvm': [False, "Build LLVM without any dependency", CUSTOM], 'minimal': [False, "Build LLVM only", CUSTOM], 'enable_rtti': [True, "Enable RTTI", CUSTOM], 'skip_all_tests': [False, "Skip running of tests", CUSTOM], From 1bac4599a8813807426b7aaafe464cfbfd8402e6 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 18 Jul 2024 14:54:51 +0200 Subject: [PATCH 040/103] Moved version check in configure step to allow `inject_checksums` and tests --- easybuild/easyblocks/l/llvm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 66988105374..ebb588835d2 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -203,10 +203,6 @@ def __init__(self, *args, **kwargs): """Initialize LLVM-specific variables.""" super(EB_LLVM, self).__init__(*args, **kwargs) - # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock - if not self.cfg['minimal'] and LooseVersion(self.version) < LooseVersion('18.1.6'): - raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) - self.llvm_src_dir = None self.llvm_obj_dir_stage1 = None self.llvm_obj_dir_stage2 = None @@ -402,6 +398,10 @@ def configure_step(self): """ Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target """ + # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock + if not self.cfg['minimal'] and LooseVersion(self.version) < LooseVersion('18.1.6'): + raise EasyBuildError("LLVM version %s is not supported, please use version 18.1.6 or newer", self.version) + # Allow running with older versions of LLVM for minimal builds in order to replace EB_LLVM easyblock gcc_version = get_software_version('GCCcore') if not self.cfg['minimal'] and LooseVersion(gcc_version) < LooseVersion('13'): From 3df55d2d18d882163267cc4c96cdf5f1556a97a7 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 18 Jul 2024 15:00:19 +0200 Subject: [PATCH 041/103] Changed build_runtimes default to allow `module-only` in CI test --- easybuild/easyblocks/l/llvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index ebb588835d2..4a72605cd72 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -189,7 +189,7 @@ def extra_options(): 'build_bolt': [False, "Build the LLVM bolt binary optimizer", CUSTOM], 'build_lld': [False, "Build the LLVM lld linker", CUSTOM], 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], - 'build_runtimes': [True, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], + 'build_runtimes': [False, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], From 659f18fb001513b11b95f6859741e212d6b5d38b Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 19 Jul 2024 13:02:06 +0200 Subject: [PATCH 042/103] Added `DEFAULT_SYSROOT` --- easybuild/easyblocks/l/llvm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 4a72605cd72..7553a8bb534 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -288,6 +288,11 @@ def __init__(self, *args, **kwargs): if self.cfg['build_bolt']: self.final_projects.append('bolt') + # Sysroot + sysroot = build_option('sysroot') + if sysroot: + general_opts['DEFAULT_SYSROOT'] = sysroot + # Build targets build_targets = self.cfg['build_targets'] if build_targets is None: From 14e2cb0119b1c80c6f16edb3a135a95494bcc115 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 25 Jul 2024 10:23:18 +0200 Subject: [PATCH 043/103] Added CMAKE_SYSROOT --- easybuild/easyblocks/l/llvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 7553a8bb534..6006c28718a 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -292,6 +292,7 @@ def __init__(self, *args, **kwargs): sysroot = build_option('sysroot') if sysroot: general_opts['DEFAULT_SYSROOT'] = sysroot + general_opts['CMAKE_SYSROOT'] = sysroot # Build targets build_targets = self.cfg['build_targets'] From 3e7645abefa85e15177a5d91482379b13db6295b Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 28 Aug 2024 12:03:35 +0200 Subject: [PATCH 044/103] Added original check for cuda/amd deps in enabling default targets --- easybuild/easyblocks/l/llvm.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 6006c28718a..23082d9e1e9 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -297,9 +297,21 @@ def __init__(self, *args, **kwargs): # Build targets build_targets = self.cfg['build_targets'] if build_targets is None: + deps = [dep['name'].lower() for dep in self.cfg.dependencies()] arch = get_cpu_architecture() try: default_targets = DEFAULT_TARGETS_MAP[arch][:] + # If CUDA is included as a dep, add NVPTX as a target + # There are (old) toolchains with CUDA as part of the toolchain + cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') + if 'cuda' in deps or cuda_toolchain: + default_targets += ['NVPTX'] + # For AMDGPU support we need ROCR-Runtime and + # ROCT-Thunk-Interface, however, since ROCT is a dependency of + # ROCR we only check for the ROCR-Runtime here + # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler + if 'rocr-runtime' in deps: + default_targets += ['AMDGPU'] self.cfg['build_targets'] = build_targets = default_targets self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) except KeyError: From fbd2fb7f7968df52984f277a4d42821e1f4dbed5 Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 28 Aug 2024 13:13:19 +0200 Subject: [PATCH 045/103] Autoenable NVPTX/AMDGPU if `cuda_compute_capabilities`/`amd_gfx_list` are specified --- easybuild/easyblocks/l/llvm.py | 51 +++++++++++++--------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 23082d9e1e9..519d9f228cf 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -294,9 +294,16 @@ def __init__(self, *args, **kwargs): general_opts['DEFAULT_SYSROOT'] = sysroot general_opts['CMAKE_SYSROOT'] = sysroot + # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): + # (1) in the easyconfig file, via the custom cuda_compute_capabilities; + # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; + cuda_cc_list = build_option('cuda_compute_capabilities') or self.cfg['cuda_compute_capabilities'] or [] + amd_gfx_list = self.cfg['amd_gfx_list'] or [] + # Build targets - build_targets = self.cfg['build_targets'] - if build_targets is None: + build_targets = self.cfg['build_targets'] or [] + if not build_targets: + self.log.debug("No build targets specified, using default detection") deps = [dep['name'].lower() for dep in self.cfg.dependencies()] arch = get_cpu_architecture() try: @@ -304,14 +311,16 @@ def __init__(self, *args, **kwargs): # If CUDA is included as a dep, add NVPTX as a target # There are (old) toolchains with CUDA as part of the toolchain cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') - if 'cuda' in deps or cuda_toolchain: + if 'cuda' in deps or cuda_toolchain or cuda_cc_list: default_targets += ['NVPTX'] + self.log.debug("NVPTX enabled by CUDA dependency/cuda_compute_capabilities") # For AMDGPU support we need ROCR-Runtime and # ROCT-Thunk-Interface, however, since ROCT is a dependency of # ROCR we only check for the ROCR-Runtime here # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler - if 'rocr-runtime' in deps: + if 'rocr-runtime' in deps or amd_gfx_list: default_targets += ['AMDGPU'] + self.log.debug("AMDGPU enabled by rocr-runtime dependency/amd_gfx_list") self.cfg['build_targets'] = build_targets = default_targets self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) except KeyError: @@ -328,33 +337,13 @@ def __init__(self, *args, **kwargs): self.build_targets = build_targets or [] - self.nvptx_cond = 'NVPTX' in self.build_targets - self.amd_cond = 'AMDGPU' in self.build_targets - self.all_cond = 'all' in self.build_targets - self.cuda_cc = [] - if self.nvptx_cond or self.all_cond: - # list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)): - # (1) in the easyconfig file, via the custom cuda_compute_capabilities; - # (2) in the EasyBuild configuration, via --cuda-compute-capabilities configuration option; - ec_cuda_cc = self.cfg['cuda_compute_capabilities'] - cfg_cuda_cc = build_option('cuda_compute_capabilities') - cuda_cc = cfg_cuda_cc or ec_cuda_cc or [] - if not cuda_cc and self.nvptx_cond: - raise EasyBuildError( - "Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'" - ) - else: - self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc] - - self.amd_gfx = [] - if self.amd_cond or self.all_cond: - self.amd_gfx = self.cfg['amd_gfx_list'] or [] - if not self.amd_gfx and self.amd_cond: - raise EasyBuildError( - "Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'" - ) - else: - self.log.info("Using AMDGPU targets: %s", ', '.join(self.amd_gfx)) + self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc_list] + if 'NVPTX' in self.build_targets and not self.cuda_cc: + raise EasyBuildError("Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'") + + self.amd_gfx = amd_gfx_list + if 'AMDGPU' in self.build_targets and not self.amd_gfx: + raise EasyBuildError("Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'") general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir From 6534d6df32b898cfa9da757dca6934ab372cff5f Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 28 Aug 2024 13:28:10 +0200 Subject: [PATCH 046/103] Add warning --- easybuild/easyblocks/l/llvm.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 519d9f228cf..96eaf1739f8 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -340,10 +340,14 @@ def __init__(self, *args, **kwargs): self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc_list] if 'NVPTX' in self.build_targets and not self.cuda_cc: raise EasyBuildError("Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'") + if self.cuda_cc and 'NVPTX' not in self.build_targets: + print_warning("CUDA compute capabilities specified, but NVPTX not in manually specified build targets.") self.amd_gfx = amd_gfx_list if 'AMDGPU' in self.build_targets and not self.amd_gfx: raise EasyBuildError("Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'") + if self.amd_gfx and 'AMDGPU' not in self.build_targets: + print_warning("`amd_gfx` specified, but AMDGPU not in manually specified build targets.") general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir From 4416cae0195e58be815366d7f6da53e7ac847820 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 5 Sep 2024 13:20:57 +0200 Subject: [PATCH 047/103] Run test suite directly in serial --- easybuild/easyblocks/l/llvm.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 96eaf1739f8..b1ebf684cce 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -667,11 +667,12 @@ def test_step(self): """Run tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: max_failed = self.cfg['test_suite_max_failed'] - self.log.info("Running test-suite with parallel jobs") - num_failed = self._para_test_step(parallel=self.cfg['parallel']) - if num_failed is None: - self.log.warning("Tests with parallel jobs failed, retrying with single job") - num_failed = self._para_test_step(parallel=1) + # self.log.info("Running test-suite with parallel jobs") + # num_failed = self._para_test_step(parallel=self.cfg['parallel']) + # if num_failed is None: + # self.log.warning("Tests with parallel jobs failed, retrying with single job") + # num_failed = self._para_test_step(parallel=1) + num_failed = self._para_test_step(parallel=1) if num_failed is None: raise EasyBuildError("Failed to extract test results from output") From 7a9905360d64c3454d114a4dd3b18d5d4443f0eb Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 12 Sep 2024 10:26:35 +0200 Subject: [PATCH 048/103] Added option to replace deprecated `GCC_INSTALL_PREFIX` --- easybuild/easyblocks/l/llvm.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index b1ebf684cce..fb243de7e5c 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -477,8 +477,22 @@ def configure_step(self): # If that doesn't work either, print error and exit if gcc_prefix is None: raise EasyBuildError("Can't find GCC or GCCcore to use") - general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix - self.log.debug("Using %s as GCC_INSTALL_PREFIX", gcc_prefix) + if LooseVersion(self.version) < LooseVersion('18'): + self.log.debug("Using GCC_INSTALL_PREFIX") + general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix + else: + # See https://github.com/llvm/llvm-project/pull/85891#issuecomment-2021370667 + self.log.debug("Using `--gcc-install-dir` in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") + arch = get_cpu_architecture() + pattern = os.path.join(gcc_prefix, 'lib', 'gcc', '%s-*' % arch, '%s' % gcc_version) + matches = glob.glob(pattern) + if not matches: + raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) + gcc_prefix = os.path.abspath(matches[0]) + general_opts['RUNTIMES_CMAKE_ARGS'] = ( + '"-DCMAKE_C_FLAGS=--gcc-install-dir=%s;-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s"' + ) % (gcc_prefix, gcc_prefix) + self.log.debug("Using %s as the gcc install location", gcc_prefix) # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by # using the environment variable which is used as-is and later checked for a falsy value when determining From 0b9dfa41ac7bc761b41e432b115a5376127b7b77 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 12 Sep 2024 13:02:12 +0200 Subject: [PATCH 049/103] Switched usage of `--gcc-install-dir` for LLVM >=19. Added config file generation and sanity check --- easybuild/easyblocks/l/llvm.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index fb243de7e5c..23c24cb3a08 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -477,7 +477,9 @@ def configure_step(self): # If that doesn't work either, print error and exit if gcc_prefix is None: raise EasyBuildError("Can't find GCC or GCCcore to use") - if LooseVersion(self.version) < LooseVersion('18'): + # --gcc-toolchain and --gcc-install-dir for flang are not supported before LLVM 19 + # https://github.com/llvm/llvm-project/pull/87360 + if LooseVersion(self.version) < LooseVersion('19'): self.log.debug("Using GCC_INSTALL_PREFIX") general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix else: @@ -492,6 +494,7 @@ def configure_step(self): general_opts['RUNTIMES_CMAKE_ARGS'] = ( '"-DCMAKE_C_FLAGS=--gcc-install-dir=%s;-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s"' ) % (gcc_prefix, gcc_prefix) + self.gcc_prefix = gcc_prefix self.log.debug("Using %s as the gcc install location", gcc_prefix) # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by @@ -729,6 +732,14 @@ def post_install_step(self): python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") shutil.copytree(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) + if LooseVersion(self.version) >= LooseVersion('18'): + bin_dir = os.path.join(self.installdir, 'bin') + prefix_str = '--gcc-install-dir=%s' % self.gcc_prefix + # Tested in LLVM 18.1.8 flang-new automatically picks up the `flang.cfg` file not `flang-new.cfg` + for comp in ['clang', 'clang++', 'flang']: + with open(os.path.join(bin_dir, '%s.cfg' % comp), 'w') as f: + f.write(prefix_str) + def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" arch = get_cpu_architecture() @@ -757,6 +768,20 @@ def banned_linked_shared_libs(self): res += ['libc++', 'libc++abi', 'libunwind'] return res + def _sanity_check_gcc_prefix(self, compilers): + """Check if the GCC prefix is correct.""" + if LooseVersion(self.version) >= LooseVersion('18'): + rgx = re.compile('Selected GCC installation: (.*)') + for comp in compilers: + cmd = "%s -v" % os.path.join(self.installdir, 'bin', comp) + out, _ = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + mch = rgx.search(out) + if mch is None: + raise EasyBuildError("Failed to extract GCC installation path from output of `%s`", cmd) + gcc_prefix = mch.group(1) + if gcc_prefix != self.gcc_prefix: + raise EasyBuildError("GCC installation path in `%s` does not match expected path", cmd) + def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): """Perform sanity checks on the installed LLVM.""" if self.cfg['build_runtimes']: @@ -783,7 +808,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F custom_commands = [ 'llvm-ar --help', 'llvm-ranlib --help', 'llvm-nm --help', 'llvm-objdump --help', ] - + gcc_prefix_compilers = [] if self.build_shared: check_lib_files += ['libLLVM.so'] @@ -801,6 +826,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'lib/cmake/clang', 'include/clang' ] custom_commands += ['llvm-config --cxxflags', 'clang --help', 'clang++ --help'] + gcc_prefix_compilers += ['clang', 'clang++'] if 'clang-tools-extra' in self.final_projects: check_bin_files += [ @@ -821,6 +847,8 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F ] check_dirs += ['lib/cmake/flang', 'include/flang'] custom_commands += ['bbc --help', 'mlir-tblgen --help', 'flang-new --help'] + gcc_prefix_compilers += ['flang-new'] + if 'lld' in self.final_projects: check_bin_files += ['ld.lld', 'lld', 'lld-link', 'wasm-ld'] check_lib_files += [ @@ -906,6 +934,8 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'dirs': check_dirs, } + self._sanity_check_gcc_prefix(gcc_prefix_compilers) + return super(EB_LLVM, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) def make_module_extra(self): From 0e7a85fdda60494a246602c465a733cb21a24267 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 12 Sep 2024 17:57:57 +0200 Subject: [PATCH 050/103] Make sure gcc_prefix works also in moduleonly and add debug feature for test suite --- easybuild/easyblocks/l/llvm.py | 82 +++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 23c24cb3a08..75729be0f0b 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -195,6 +195,7 @@ def extra_options(): 'usepolly': [False, "Build Clang with polly", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], + 'debug_tests': [True, "Enable verbose output for tests", CUSTOM], }) return extra_vars @@ -214,6 +215,7 @@ def __init__(self, *args, **kwargs): else: self.final_projects = ['llvm'] self.final_runtimes = [] + self.gcc_prefix = None # Shared self.build_shared = self.cfg.get('build_shared_libs', False) @@ -348,7 +350,6 @@ def __init__(self, *args, **kwargs): raise EasyBuildError("Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'") if self.amd_gfx and 'AMDGPU' not in self.build_targets: print_warning("`amd_gfx` specified, but AMDGPU not in manually specified build targets.") - general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir if self.toolchain.options['pic']: @@ -398,13 +399,53 @@ def _configure_final_build(self): self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' # Make sure tests are not running with more than `--parallel` tasks - self._cmakeopts['LLVM_LIT_ARGS'] = '"-j %s"' % self.cfg['parallel'] + parallel = self.cfg['parallel'] + if not build_option('mpi_tests'): + parallel = 1 + lit_args = ['-j %s' % parallel] + if self.cfg['debug_tests']: + lit_args += ['-v'] + self._cmakeopts['LLVM_LIT_ARGS'] = '"%s"' % ' '.join(lit_args) if self.cfg['usepolly']: self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' if not self.cfg['skip_all_tests']: self._cmakeopts['LLVM_INCLUDE_TESTS'] = 'ON' self._cmakeopts['LLVM_BUILD_TESTS'] = 'ON' + def _set_gcc_prefix(self): + """Set the GCC prefix for the build.""" + if self.gcc_prefix is None: + arch = get_cpu_architecture() + gcc_root = get_software_root('GCCcore') + gcc_version = get_software_version('GCCcore') + # If that doesn't work, try with GCC + if gcc_root is None: + gcc_root = get_software_root('GCC') + gcc_version = get_software_version('GCC') + # If that doesn't work either, print error and exit + if gcc_root is None: + raise EasyBuildError("Can't find GCC or GCCcore to use") + + pattern = os.path.join(gcc_root, 'lib', 'gcc', '%s-*' % arch, '%s' % gcc_version) + matches = glob.glob(pattern) + if not matches: + raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) + gcc_prefix = os.path.abspath(matches[0]) + + # --gcc-toolchain and --gcc-install-dir for flang are not supported before LLVM 19 + # https://github.com/llvm/llvm-project/pull/87360 + if LooseVersion(self.version) < LooseVersion('19'): + self.log.debug("Using GCC_INSTALL_PREFIX") + general_opts['GCC_INSTALL_PREFIX'] = gcc_root + else: + # See https://github.com/llvm/llvm-project/pull/85891#issuecomment-2021370667 + self.log.debug("Using `--gcc-install-dir` in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") + general_opts['RUNTIMES_CMAKE_ARGS'] = ( + '"-DCMAKE_C_FLAGS=--gcc-install-dir=%s;-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s"' + ) % (gcc_prefix, gcc_prefix) + self.gcc_prefix = gcc_prefix + self.log.debug("Using %s as the gcc install location", gcc_prefix) + def configure_step(self): """ Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target @@ -470,32 +511,7 @@ def configure_step(self): regex_subs.append((r'add_subdirectory\(bindings/python/tests\)', '')) apply_regex_substitutions(cmakelists_tests, regex_subs) - gcc_prefix = get_software_root('GCCcore') - # If that doesn't work, try with GCC - if gcc_prefix is None: - gcc_prefix = get_software_root('GCC') - # If that doesn't work either, print error and exit - if gcc_prefix is None: - raise EasyBuildError("Can't find GCC or GCCcore to use") - # --gcc-toolchain and --gcc-install-dir for flang are not supported before LLVM 19 - # https://github.com/llvm/llvm-project/pull/87360 - if LooseVersion(self.version) < LooseVersion('19'): - self.log.debug("Using GCC_INSTALL_PREFIX") - general_opts['GCC_INSTALL_PREFIX'] = gcc_prefix - else: - # See https://github.com/llvm/llvm-project/pull/85891#issuecomment-2021370667 - self.log.debug("Using `--gcc-install-dir` in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") - arch = get_cpu_architecture() - pattern = os.path.join(gcc_prefix, 'lib', 'gcc', '%s-*' % arch, '%s' % gcc_version) - matches = glob.glob(pattern) - if not matches: - raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) - gcc_prefix = os.path.abspath(matches[0]) - general_opts['RUNTIMES_CMAKE_ARGS'] = ( - '"-DCMAKE_C_FLAGS=--gcc-install-dir=%s;-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s"' - ) % (gcc_prefix, gcc_prefix) - self.gcc_prefix = gcc_prefix - self.log.debug("Using %s as the gcc install location", gcc_prefix) + self._set_gcc_prefix() # If we don't want to build with CUDA (not in dependencies) trick CMakes FindCUDA module into not finding it by # using the environment variable which is used as-is and later checked for a falsy value when determining @@ -732,7 +748,7 @@ def post_install_step(self): python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") shutil.copytree(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) - if LooseVersion(self.version) >= LooseVersion('18'): + if LooseVersion(self.version) >= LooseVersion('19'): bin_dir = os.path.join(self.installdir, 'bin') prefix_str = '--gcc-install-dir=%s' % self.gcc_prefix # Tested in LLVM 18.1.8 flang-new automatically picks up the `flang.cfg` file not `flang-new.cfg` @@ -770,17 +786,21 @@ def banned_linked_shared_libs(self): def _sanity_check_gcc_prefix(self, compilers): """Check if the GCC prefix is correct.""" - if LooseVersion(self.version) >= LooseVersion('18'): + if LooseVersion(self.version) >= LooseVersion('19'): + self._set_gcc_prefix() # Ensure GCC prefix is set in case of module/sanity-only rgx = re.compile('Selected GCC installation: (.*)') for comp in compilers: cmd = "%s -v" % os.path.join(self.installdir, 'bin', comp) out, _ = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) mch = rgx.search(out) if mch is None: + self.log.debug(out) raise EasyBuildError("Failed to extract GCC installation path from output of `%s`", cmd) gcc_prefix = mch.group(1) if gcc_prefix != self.gcc_prefix: - raise EasyBuildError("GCC installation path in `%s` does not match expected path", cmd) + raise EasyBuildError( + "GCC installation path `%s` does not match expected path `%s`", gcc_prefix, self.gcc_prefix + ) def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): """Perform sanity checks on the installed LLVM.""" From d2fff9b04f98c57cf05a4bdf405f785964313357 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 26 Sep 2024 10:30:55 +0200 Subject: [PATCH 051/103] Support for python<3.8 --- easybuild/easyblocks/l/llvm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 75729be0f0b..3066b6451ed 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -746,7 +746,10 @@ def post_install_step(self): shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") - shutil.copytree(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) + try: + shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) + except FileExistsError: + pass if LooseVersion(self.version) >= LooseVersion('19'): bin_dir = os.path.join(self.installdir, 'bin') From 1b0959127be1bdee002a391845b5c83409ebf4de Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 30 Sep 2024 10:41:33 +0200 Subject: [PATCH 052/103] Use `copy_tree` instead of `shutil.copytree` directly --- easybuild/easyblocks/l/llvm.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 3066b6451ed..b77ac1fd7a1 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -37,7 +37,6 @@ import glob import os import re -import shutil from easybuild.framework.easyconfig import CUSTOM from easybuild.toolchains.compiler.clang import Clang @@ -46,7 +45,7 @@ from easybuild.tools.config import build_option from easybuild.tools.environment import setvar from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, - mkdir, symlink, which) + copy_dir, mkdir, symlink, which) from easybuild.tools.modules import get_software_root, get_software_version from easybuild.tools.run import run_cmd from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, @@ -124,7 +123,7 @@ general_opts = { # If EB is launched from a venv, avoid giving priority to the venv's python - 'Python3_FIND_VIRTUALENV': 'STANDARD', + # 'Python3_FIND_VIRTUALENV': 'STANDARD', 'LLVM_INSTALL_UTILS': 'ON', 'LLVM_INCLUDE_BENCHMARKS': 'OFF', 'CMAKE_VERBOSE_MAKEFILE': 'ON', @@ -644,6 +643,7 @@ def build_step(self, verbose=False, path=None): print_msg("Building stage 1/1") change_dir(self.llvm_obj_dir_stage1) super(EB_LLVM, self).build_step(verbose, path) + # import shutil # change_dir(self.builddir) # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") # shutil.rmtree('llvm.obj.1', ignore_errors=True) @@ -743,13 +743,10 @@ def post_install_step(self): if self.cfg['python_bindings']: python_bindings_source_dir = os.path.join(self.llvm_src_dir, "clang", "bindings", "python") python_bindins_target_dir = os.path.join(self.installdir, 'lib', 'python') - shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) + copy_dir(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") - try: - shutil.copytree(python_bindings_source_dir, python_bindins_target_dir) - except FileExistsError: - pass + copy_dir(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) if LooseVersion(self.version) >= LooseVersion('19'): bin_dir = os.path.join(self.installdir, 'bin') From 0c5d4088e7d5bab8997932218d459a59be4ab565 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 30 Sep 2024 18:06:45 +0200 Subject: [PATCH 053/103] Added possibility to time out tests --- easybuild/easyblocks/l/llvm.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index b77ac1fd7a1..d067e71cb98 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -194,6 +194,8 @@ def extra_options(): 'usepolly': [False, "Build Clang with polly", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], + 'test_suite_timeout_total': [None, "Timeout for total running time of the testsuite", CUSTOM], + 'test_suite_timeout_single': [None, "Timeout for each individual test in the test suite", CUSTOM], 'debug_tests': [True, "Enable verbose output for tests", CUSTOM], }) @@ -404,7 +406,14 @@ def _configure_final_build(self): lit_args = ['-j %s' % parallel] if self.cfg['debug_tests']: lit_args += ['-v'] + timeout_single = self.cfg['test_suite_timeout_single'] + if timeout_single: + lit_args += ['--timeout', str(timeout_single)] + timeout_total = self.cfg['test_suite_timeout_total'] + if timeout_total: + lit_args += ['--max-time', str(timeout_total)] self._cmakeopts['LLVM_LIT_ARGS'] = '"%s"' % ' '.join(lit_args) + if self.cfg['usepolly']: self._cmakeopts['LLVM_POLLY_LINK_INTO_TOOLS'] = 'ON' if not self.cfg['skip_all_tests']: @@ -694,6 +703,15 @@ def _para_test_step(self, parallel=1): else: num_failed = int(mch.group(1)) + if num_failed is not None: + num_timed_out = 0 + rgx_timed_out = re.compile(r'^ +Timed Out +: +([0-9]+)', flags=re.MULTILINE) + mch = rgx_timed_out.search(out) + if mch is not None: + num_timed_out = int(mch.group(1)) + self.log.info("Tests timed out: %s", num_timed_out) + num_failed += num_timed_out + return num_failed def test_step(self): From 5d5bf1fbe50d65cd1552ba84cc199f24891473d8 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 3 Oct 2024 10:51:51 +0200 Subject: [PATCH 054/103] Revert --- easybuild/easyblocks/l/llvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index d067e71cb98..75a3369311b 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -123,7 +123,7 @@ general_opts = { # If EB is launched from a venv, avoid giving priority to the venv's python - # 'Python3_FIND_VIRTUALENV': 'STANDARD', + 'Python3_FIND_VIRTUALENV': 'STANDARD', 'LLVM_INSTALL_UTILS': 'ON', 'LLVM_INCLUDE_BENCHMARKS': 'OFF', 'CMAKE_VERBOSE_MAKEFILE': 'ON', From 980cd066553f2dde9e4893a8b56dd68ec4bda6e1 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 3 Oct 2024 16:46:41 +0200 Subject: [PATCH 055/103] Changed `runtimes_cmake_args` into a list --- easybuild/easyblocks/l/llvm.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 75a3369311b..baa0e04c64a 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -217,6 +217,7 @@ def __init__(self, *args, **kwargs): self.final_projects = ['llvm'] self.final_runtimes = [] self.gcc_prefix = None + self.runtimes_cmake_args = [] # Shared self.build_shared = self.cfg.get('build_shared_libs', False) @@ -377,6 +378,9 @@ def _configure_general_build(self): self._cmakeopts.update(general_opts) + if self.runtimes_cmake_args: + self._cmakeopts['RUNTIMES_CMAKE_ARGS'] = '"%s"' % ';'.join(self.runtimes_cmake_args) + def _configure_intermediate_build(self): """Configure the intermediate stages of the build.""" self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) @@ -448,9 +452,11 @@ def _set_gcc_prefix(self): else: # See https://github.com/llvm/llvm-project/pull/85891#issuecomment-2021370667 self.log.debug("Using `--gcc-install-dir` in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") - general_opts['RUNTIMES_CMAKE_ARGS'] = ( - '"-DCMAKE_C_FLAGS=--gcc-install-dir=%s;-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s"' - ) % (gcc_prefix, gcc_prefix) + self.runtimes_cmake_args += [ + '-DCMAKE_C_FLAGS=--gcc-install-dir=%s' % gcc_prefix, + '-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s' % gcc_prefix + ] + self.gcc_prefix = gcc_prefix self.log.debug("Using %s as the gcc install location", gcc_prefix) @@ -500,6 +506,16 @@ def configure_step(self): general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' # general_opts['LIBXML2_ROOT'] = xml2_root + python_root = get_software_root('Python') + if python_root: + python_bin = os.path.join(python_root, 'bin', 'python') + general_opts['Python_EXECUTABLE'] = python_bin + general_opts['Python3_EXECUTABLE'] = python_bin + self.runtimes_cmake_args += [ + '-DPython_EXECUTABLE=%s' % python_bin, + '-DPython3_EXECUTABLE=%s' % python_bin + ] + self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: self._configure_intermediate_build() From 944332bfeac29c3fdacad69bdbab95e935965f6a Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 4 Oct 2024 13:50:55 +0200 Subject: [PATCH 056/103] Added check for test timeouts and psutil --- easybuild/easyblocks/l/llvm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index baa0e04c64a..feac6f0b5fc 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -479,6 +479,11 @@ def configure_step(self): if not self.cfg['skip_all_tests']: raise EasyBuildError("Can't find `lit`, needed for running tests-suite") + if not self.cfg['skip_all_tests'] and (self.cfg['test_suite_timeout_single'] or self.cfg['test_suite_timeout_total']): + psutil_root = get_software_root('psutil') + if not psutil_root: + raise EasyBuildError("Can't find `psutil`, needed for running tests-suite with timeout") + # Parallel build self.make_parallel_opts = "" if self.cfg['parallel']: From c95c69ebed50f05195a77337c16cc3775b80474e Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 4 Oct 2024 13:53:41 +0200 Subject: [PATCH 057/103] lint --- easybuild/easyblocks/l/llvm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index feac6f0b5fc..5e9a858807f 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -479,7 +479,8 @@ def configure_step(self): if not self.cfg['skip_all_tests']: raise EasyBuildError("Can't find `lit`, needed for running tests-suite") - if not self.cfg['skip_all_tests'] and (self.cfg['test_suite_timeout_single'] or self.cfg['test_suite_timeout_total']): + timeouts = self.cfg['test_suite_timeout_single'] or self.cfg['test_suite_timeout_total'] + if not self.cfg['skip_all_tests'] and timeouts: psutil_root = get_software_root('psutil') if not psutil_root: raise EasyBuildError("Can't find `psutil`, needed for running tests-suite with timeout") From 20edf1970212a823333478976d447bc8952f20bf Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 7 Oct 2024 13:49:35 +0200 Subject: [PATCH 058/103] Fix missing `self` --- easybuild/easyblocks/l/llvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 5e9a858807f..0f105b1bebe 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -458,7 +458,7 @@ def _set_gcc_prefix(self): ] self.gcc_prefix = gcc_prefix - self.log.debug("Using %s as the gcc install location", gcc_prefix) + self.log.debug("Using %s as the gcc install location", self.gcc_prefix) def configure_step(self): """ From f10ff75120da3e910384611d100006cbcf9fe191 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 7 Oct 2024 13:49:52 +0200 Subject: [PATCH 059/103] Changes for LLVM 19 --- easybuild/easyblocks/l/llvm.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 0f105b1bebe..62bbee611c4 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -270,6 +270,11 @@ def __init__(self, *args, **kwargs): self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] if self.cfg['build_openmp']: self.final_projects.append('openmp') + # LLVM 19 added a new runtime target for explicit offloading + # https://discourse.llvm.org/t/llvm-19-1-0-no-library-libomptarget-nvptx-sm-80-bc-found/81343 + if LooseVersion(self.version) >= LooseVersion('19'): + self.log.debug("Explicitly enabling OpenMP offloading for LLVM >= 19") + self.final_runtimes.append('offload') if self.cfg['build_openmp_tools']: if not self.cfg['build_openmp']: raise EasyBuildError("Building OpenMP tools requires building OpenMP runtime") @@ -899,7 +904,10 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F ] check_dirs += ['include/clang-tidy'] if 'flang' in self.final_projects: - check_bin_files += ['bbc', 'flang-new', 'flang-to-external-fc', 'f18-parse-demo', 'fir-opt', 'tco'] + if LooseVersion(self.version) < LooseVersion('19'): + check_bin_files += ['bbc', 'flang-new', 'flang-to-external-fc', 'f18-parse-demo', 'fir-opt', 'tco'] + else: + check_bin_files += ['bbc', 'flang-new', 'f18-parse-demo', 'fir-opt', 'tco'] check_lib_files += [ 'libFortranRuntime.a', 'libFortranSemantics.a', 'libFortranLower.a', 'libFortranParser.a', 'libFIRCodeGen.a', 'libflangFrontend.a', 'libFortranCommon.a', 'libFortranDecimal.a', @@ -965,14 +973,18 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_bin_files += ['llvm-bolt', 'llvm-boltdiff', 'llvm-bolt-heatmap'] check_lib_files += ['libbolt_rt_instr.a'] if 'openmp' in self.final_projects: - check_lib_files += ['libomp.so', 'libompd.so'] - check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] - if 'NVPTX' in self.cfg['build_targets']: - check_lib_files += ['libomptarget.rtl.cuda.so'] - check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] - if 'AMDGPU' in self.cfg['build_targets']: - check_lib_files += ['libomptarget.rtl.amdgpu.so'] - check_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] + if LooseVersion(self.version) < LooseVersion('19'): + check_lib_files += ['libomp.so', 'libompd.so'] + else: + check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libomp.so', 'libompd.so']] + if LooseVersion(self.version) < LooseVersion('19') or 'offload' in self.final_projects: + check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] + if 'NVPTX' in self.cfg['build_targets']: + check_lib_files += ['libomptarget.rtl.cuda.so'] + check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] + if 'AMDGPU' in self.cfg['build_targets']: + check_lib_files += ['libomptarget.rtl.amdgpu.so'] + check_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] if self.cfg['python_bindings']: From 08376411ce32232715e74455adc8cadb58802633 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 7 Oct 2024 16:45:49 +0200 Subject: [PATCH 060/103] Fix for sanity check with linked LLVM runtimes --- easybuild/easyblocks/l/llvm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 62bbee611c4..9c22e9a89dd 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -1006,7 +1006,9 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'dirs': check_dirs, } - self._sanity_check_gcc_prefix(gcc_prefix_compilers) + # Required for `clang -v` to work if linked to LLVM runtimes + with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)): + self._sanity_check_gcc_prefix(gcc_prefix_compilers) return super(EB_LLVM, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) From 0a0932e804ee4a17f2b443e2090695040383ffad Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 8 Oct 2024 15:37:48 +0200 Subject: [PATCH 061/103] Check `final_runtimes` --- easybuild/easyblocks/l/llvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 9c22e9a89dd..ab5a40fb6f5 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -977,7 +977,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_lib_files += ['libomp.so', 'libompd.so'] else: check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libomp.so', 'libompd.so']] - if LooseVersion(self.version) < LooseVersion('19') or 'offload' in self.final_projects: + if LooseVersion(self.version) < LooseVersion('19') or 'offload' in self.final_runtimes: check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] if 'NVPTX' in self.cfg['build_targets']: check_lib_files += ['libomptarget.rtl.cuda.so'] From ff7e363077e73d731fe7d82293b5c0b54ce5a60d Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 10 Oct 2024 10:31:09 +0200 Subject: [PATCH 062/103] Imrpoved `runtimes_cmake_args` and fix for `full_llvm` --- easybuild/easyblocks/l/llvm.py | 75 ++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index ab5a40fb6f5..5760ca88e31 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -217,7 +217,12 @@ def __init__(self, *args, **kwargs): self.final_projects = ['llvm'] self.final_runtimes = [] self.gcc_prefix = None - self.runtimes_cmake_args = [] + self.runtimes_cmake_args = { + 'CMAKE_C_FLAGS': [], + 'CMAKE_CXX_FLAGS': [], + 'CMAKE_EXE_LINKER_FLAGS': [], + } + # self._added_librt = None # Shared self.build_shared = self.cfg.get('build_shared_libs', False) @@ -368,6 +373,17 @@ def __init__(self, *args, **kwargs): self._cfgopts = list(filter(None, self.cfg.get('configopts', '').split())) self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) + def _add_cmake_runtime_args(self): + """Generate the value for `RUNTIMES_CMAKE_ARGS` and add it to the cmake options.""" + if self.runtimes_cmake_args: + tmp_list = [] + for key, val in self.runtimes_cmake_args.items(): + if isinstance(val, list): + val = ' '.join(val) + if val: + tmp_list += ['-D%s=%s' % (key, val)] + self._cmakeopts['RUNTIMES_CMAKE_ARGS'] = '"%s"' % ';'.join(tmp_list) + def _configure_general_build(self): """General configuration step for LLVM.""" self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' @@ -382,9 +398,8 @@ def _configure_general_build(self): self._cmakeopts['LLVM_Z3_INSTALL_DIR'] = z3_root self._cmakeopts.update(general_opts) + self._add_cmake_runtime_args() - if self.runtimes_cmake_args: - self._cmakeopts['RUNTIMES_CMAKE_ARGS'] = '"%s"' % ';'.join(self.runtimes_cmake_args) def _configure_intermediate_build(self): """Configure the intermediate stages of the build.""" @@ -457,10 +472,8 @@ def _set_gcc_prefix(self): else: # See https://github.com/llvm/llvm-project/pull/85891#issuecomment-2021370667 self.log.debug("Using `--gcc-install-dir` in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") - self.runtimes_cmake_args += [ - '-DCMAKE_C_FLAGS=--gcc-install-dir=%s' % gcc_prefix, - '-DCMAKE_CXX_FLAGS=--gcc-install-dir=%s' % gcc_prefix - ] + self.runtimes_cmake_args['CMAKE_C_FLAGS'] += ['--gcc-install-dir=%s' % gcc_prefix] + self.runtimes_cmake_args['CMAKE_CXX_FLAGS'] += ['--gcc-install-dir=%s' % gcc_prefix] self.gcc_prefix = gcc_prefix self.log.debug("Using %s as the gcc install location", self.gcc_prefix) @@ -522,10 +535,8 @@ def configure_step(self): python_bin = os.path.join(python_root, 'bin', 'python') general_opts['Python_EXECUTABLE'] = python_bin general_opts['Python3_EXECUTABLE'] = python_bin - self.runtimes_cmake_args += [ - '-DPython_EXECUTABLE=%s' % python_bin, - '-DPython3_EXECUTABLE=%s' % python_bin - ] + self.runtimes_cmake_args['Python_EXECUTABLE'] = python_bin + self.runtimes_cmake_args['Python3_EXECUTABLE'] = python_bin self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: @@ -647,6 +658,17 @@ def build_with_prev_stage(self, prev_dir, stage_dir): setvar('CFLAGS', "%s %s" % (cflags, '-Wno-unused-command-line-argument')) setvar('CXXFLAGS', "%s %s" % (cxxflags, '-Wno-unused-command-line-argument')) + if self.full_llvm: + # See https://github.com/llvm/llvm-project/issues/111667 + to_add = '--unwindlib=none' + # for flags in ['CMAKE_C_FLAGS', 'CMAKE_CXX_FLAGS']: + for flags in ['CMAKE_EXE_LINKER_FLAGS']: + ptr = self.runtimes_cmake_args[flags] + if to_add not in ptr: + ptr.append(to_add) + + self._add_cmake_runtime_args() + # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) clang = which('clang') clangxx = which('clang++') @@ -973,18 +995,29 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_bin_files += ['llvm-bolt', 'llvm-boltdiff', 'llvm-bolt-heatmap'] check_lib_files += ['libbolt_rt_instr.a'] if 'openmp' in self.final_projects: + omp_lib_files = [] + omp_lib_files += ['libomp.so', 'libompd.so'] + # Judging from the build process/logs of LLVM 19, the omptarget plugins (rtl..so) are now built + # as static libraries and linked into the libomptarget.so shared library + omp_lib_files += ['libomptarget.so'] if LooseVersion(self.version) < LooseVersion('19'): - check_lib_files += ['libomp.so', 'libompd.so'] + ['libomptarget.rtl.%s.so' % arch] + if 'NVPTX' in self.cfg['build_targets']: + if LooseVersion(self.version) < LooseVersion('19'): + omp_lib_files += ['libomptarget.rtl.cuda.so'] + omp_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] + if 'AMDGPU' in self.cfg['build_targets']: + if LooseVersion(self.version) < LooseVersion('19'): + omp_lib_files += ['libomptarget.rtl.amdgpu.so'] + omp_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] + + if LooseVersion(self.version) < LooseVersion('19'): + # Before LLVM 19, omp related libraries are installed under `ROOT/lib`` + check_lib_files += omp_lib_files else: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libomp.so', 'libompd.so']] - if LooseVersion(self.version) < LooseVersion('19') or 'offload' in self.final_runtimes: - check_lib_files += ['libomptarget.so', 'libomptarget.rtl.%s.so' % arch] - if 'NVPTX' in self.cfg['build_targets']: - check_lib_files += ['libomptarget.rtl.cuda.so'] - check_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] - if 'AMDGPU' in self.cfg['build_targets']: - check_lib_files += ['libomptarget.rtl.amdgpu.so'] - check_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] + # Starting from LLVM 19, omp related libraries are installed the runtime library directory + check_files += [os.path.join(lib_dir_runtime, _) for _ in omp_lib_files] + if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] if self.cfg['python_bindings']: From db547c0d63aa14b435353475f79a01989ac37110 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 10 Oct 2024 10:32:52 +0200 Subject: [PATCH 063/103] lint --- easybuild/easyblocks/l/llvm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 5760ca88e31..84d23754f7f 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -400,7 +400,6 @@ def _configure_general_build(self): self._cmakeopts.update(general_opts) self._add_cmake_runtime_args() - def _configure_intermediate_build(self): """Configure the intermediate stages of the build.""" self._cmakeopts['LLVM_ENABLE_PROJECTS'] = '"%s"' % ';'.join(self.intermediate_projects) From e10f8c6e210a4c3ee237bc8423e1174d11bcbbc9 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 14 Oct 2024 12:23:06 +0200 Subject: [PATCH 064/103] Modularized `gcc_prefix` functionalities --- easybuild/easyblocks/l/llvm.py | 92 +++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 84d23754f7f..2d6f9673c18 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -148,6 +148,52 @@ def _wrap_env(path="", ld_path=""): setvar('PATH', orig_path) setvar('LD_LIBRARY_PATH', orig_ld_library_path) +def get_gcc_prefix(): + """Get the GCC prefix for the build.""" + arch = get_cpu_architecture() + gcc_root = get_software_root('GCCcore') + gcc_version = get_software_version('GCCcore') + # If that doesn't work, try with GCC + if gcc_root is None: + gcc_root = get_software_root('GCC') + gcc_version = get_software_version('GCC') + # If that doesn't work either, print error and exit + if gcc_root is None: + raise EasyBuildError("Can't find GCC or GCCcore to use") + + pattern = os.path.join(gcc_root, 'lib', 'gcc', '%s-*' % arch, '%s' % gcc_version) + matches = glob.glob(pattern) + if not matches: + raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) + gcc_prefix = os.path.abspath(matches[0]) + + return gcc_root, gcc_prefix + + +def create_compiler_config_file(compilers, gcc_prefix, installdir): + """Create a config file for the compiler to point to the correct GCC installation.""" + bin_dir = os.path.join(installdir, 'bin') + prefix_str = '--gcc-install-dir=%s' % gcc_prefix + for comp in compilers: + with open(os.path.join(bin_dir, '%s.cfg' % comp), 'w') as f: + f.write(prefix_str) + + +def sanity_check_gcc_prefix(compilers, gcc_prefix, installdir): + """Check if the GCC prefix of the compiler is correct""" + rgx = re.compile('Selected GCC installation: (.*)') + for comp in compilers: + cmd = "%s -v" % os.path.join(installdir, 'bin', comp) + out, _ = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + mch = rgx.search(out) + if mch is None: + raise EasyBuildError("Failed to extract GCC installation path from output of `%s`: %s", cmd, out) + check_prefix = mch.group(1) + if check_prefix != gcc_prefix: + raise EasyBuildError( + "GCC installation path `%s` does not match expected path `%s`", check_prefix, gcc_prefix + ) + class EB_LLVM(CMakeMake): """ @@ -446,22 +492,7 @@ def _configure_final_build(self): def _set_gcc_prefix(self): """Set the GCC prefix for the build.""" if self.gcc_prefix is None: - arch = get_cpu_architecture() - gcc_root = get_software_root('GCCcore') - gcc_version = get_software_version('GCCcore') - # If that doesn't work, try with GCC - if gcc_root is None: - gcc_root = get_software_root('GCC') - gcc_version = get_software_version('GCC') - # If that doesn't work either, print error and exit - if gcc_root is None: - raise EasyBuildError("Can't find GCC or GCCcore to use") - - pattern = os.path.join(gcc_root, 'lib', 'gcc', '%s-*' % arch, '%s' % gcc_version) - matches = glob.glob(pattern) - if not matches: - raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) - gcc_prefix = os.path.abspath(matches[0]) + gcc_root, gcc_prefix = get_gcc_prefix() # --gcc-toolchain and --gcc-install-dir for flang are not supported before LLVM 19 # https://github.com/llvm/llvm-project/pull/87360 @@ -815,12 +846,9 @@ def post_install_step(self): copy_dir(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) if LooseVersion(self.version) >= LooseVersion('19'): - bin_dir = os.path.join(self.installdir, 'bin') - prefix_str = '--gcc-install-dir=%s' % self.gcc_prefix - # Tested in LLVM 18.1.8 flang-new automatically picks up the `flang.cfg` file not `flang-new.cfg` - for comp in ['clang', 'clang++', 'flang']: - with open(os.path.join(bin_dir, '%s.cfg' % comp), 'w') as f: - f.write(prefix_str) + # For GCC aware installation create config files in order to point to the correct GCC installation + # Required as GCC_INSTALL_PREFIX was removed (see https://github.com/llvm/llvm-project/pull/87360) + create_compiler_config_file(['clang', 'clang++', 'flang'], self.gcc_prefix, self.installdir) def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" @@ -850,24 +878,6 @@ def banned_linked_shared_libs(self): res += ['libc++', 'libc++abi', 'libunwind'] return res - def _sanity_check_gcc_prefix(self, compilers): - """Check if the GCC prefix is correct.""" - if LooseVersion(self.version) >= LooseVersion('19'): - self._set_gcc_prefix() # Ensure GCC prefix is set in case of module/sanity-only - rgx = re.compile('Selected GCC installation: (.*)') - for comp in compilers: - cmd = "%s -v" % os.path.join(self.installdir, 'bin', comp) - out, _ = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) - mch = rgx.search(out) - if mch is None: - self.log.debug(out) - raise EasyBuildError("Failed to extract GCC installation path from output of `%s`", cmd) - gcc_prefix = mch.group(1) - if gcc_prefix != self.gcc_prefix: - raise EasyBuildError( - "GCC installation path `%s` does not match expected path `%s`", gcc_prefix, self.gcc_prefix - ) - def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): """Perform sanity checks on the installed LLVM.""" if self.cfg['build_runtimes']: @@ -1040,7 +1050,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F # Required for `clang -v` to work if linked to LLVM runtimes with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)): - self._sanity_check_gcc_prefix(gcc_prefix_compilers) + sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) return super(EB_LLVM, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) From 224aaca3ac9a986f0d0a06e2efe0d903055aeef8 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 14 Oct 2024 15:58:52 +0200 Subject: [PATCH 065/103] lint --- easybuild/easyblocks/l/llvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 2d6f9673c18..675e928169e 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -148,6 +148,7 @@ def _wrap_env(path="", ld_path=""): setvar('PATH', orig_path) setvar('LD_LIBRARY_PATH', orig_ld_library_path) + def get_gcc_prefix(): """Get the GCC prefix for the build.""" arch = get_cpu_architecture() From 8938c607a7d2eebd177676e51023338332f0aefc Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 14 Oct 2024 16:34:24 +0200 Subject: [PATCH 066/103] Fix for minimal builds --- easybuild/easyblocks/l/llvm.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 675e928169e..575b38ca0d8 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -881,6 +881,7 @@ def banned_linked_shared_libs(self): def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): """Perform sanity checks on the installed LLVM.""" + lib_dir_runtime = None if self.cfg['build_runtimes']: lib_dir_runtime = self.get_runtime_lib_path(self.installdir, fail_ok=False) shlib_ext = '.' + get_shared_lib_ext() @@ -1049,8 +1050,11 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'dirs': check_dirs, } - # Required for `clang -v` to work if linked to LLVM runtimes - with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)): + if lib_dir_runtime: + # Required for `clang -v` to work if linked to LLVM runtimes + with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)): + sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) + else: sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) return super(EB_LLVM, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) From 94b960be643a2fda60067a73d0fbad2c5ca98d25 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 15 Oct 2024 13:09:10 +0200 Subject: [PATCH 067/103] Ensure compilers at every stage are `GCC_PREFIX` aware --- easybuild/easyblocks/l/llvm.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 575b38ca0d8..f020b89cb58 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -215,6 +215,8 @@ class EB_LLVM(CMakeMake): 'usepolly', ] + cfg_compilers = ['clang', 'clang++', 'flang'] + @staticmethod def extra_options(): extra_vars = CMakeMake.extra_options() @@ -700,6 +702,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self._add_cmake_runtime_args() + # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) clang = which('clang') clangxx = which('clang++') @@ -720,6 +723,12 @@ def build_with_prev_stage(self, prev_dir, stage_dir): cmd = "make %s VERBOSE=1" % self.make_parallel_opts run_cmd(cmd, log_all=True) + # Also runs of the intermediate step compilers should be made aware of the GCC installation + if LooseVersion(self.version) >= LooseVersion('19'): + self._set_gcc_prefix() + # Does not matter if flang is not built, the config file will not be used + create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, stage_dir) + change_dir(curdir) def build_step(self, verbose=False, path=None): @@ -849,7 +858,8 @@ def post_install_step(self): if LooseVersion(self.version) >= LooseVersion('19'): # For GCC aware installation create config files in order to point to the correct GCC installation # Required as GCC_INSTALL_PREFIX was removed (see https://github.com/llvm/llvm-project/pull/87360) - create_compiler_config_file(['clang', 'clang++', 'flang'], self.gcc_prefix, self.installdir) + self._set_gcc_prefix() + create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.installdir) def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" From abc9c340d640e7082db1562c1d6dbb5d02bf4180 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 15 Oct 2024 13:41:31 +0200 Subject: [PATCH 068/103] lint --- easybuild/easyblocks/l/llvm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index f020b89cb58..5a344c045e6 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -702,7 +702,6 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self._add_cmake_runtime_args() - # determine full path to clang/clang++ (which may be wrapper scripts in case of RPATH linking) clang = which('clang') clangxx = which('clang++') From 4b16c72aca17a14e24d07947ac69ea15161ab88c Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 15 Oct 2024 14:51:16 +0200 Subject: [PATCH 069/103] Ensure stage1 compilers and test suite are run with correct `GCC` --- easybuild/easyblocks/l/llvm.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 5a344c045e6..705cf00557a 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -711,6 +711,11 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self._cmakeopts['CMAKE_ASM_COMPILER'] = clang self._cmakeopts['CMAKE_ASM_COMPILER_ID'] = 'Clang' + # Also runs of the intermediate step compilers should be made aware of the GCC installation + if LooseVersion(self.version) >= LooseVersion('19'): + self._set_gcc_prefix() + create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, prev_dir) + self.add_cmake_opts() change_dir(stage_dir) @@ -722,12 +727,6 @@ def build_with_prev_stage(self, prev_dir, stage_dir): cmd = "make %s VERBOSE=1" % self.make_parallel_opts run_cmd(cmd, log_all=True) - # Also runs of the intermediate step compilers should be made aware of the GCC installation - if LooseVersion(self.version) >= LooseVersion('19'): - self._set_gcc_prefix() - # Does not matter if flang is not built, the config file will not be used - create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, stage_dir) - change_dir(curdir) def build_step(self, verbose=False, path=None): @@ -805,6 +804,10 @@ def _para_test_step(self, parallel=1): def test_step(self): """Run tests on final stage (unless disabled).""" if not self.cfg['skip_all_tests']: + # Also runs of test suite compilers should be made aware of the GCC installation + if LooseVersion(self.version) >= LooseVersion('19'): + self._set_gcc_prefix() + create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.final_dir) max_failed = self.cfg['test_suite_max_failed'] # self.log.info("Running test-suite with parallel jobs") # num_failed = self._para_test_step(parallel=self.cfg['parallel']) From c13a05bd8c51e6351cee20d64179dd85b19076ce Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 18 Oct 2024 11:03:47 +0200 Subject: [PATCH 070/103] Ensure gcc_prefix is set in module-only --- easybuild/easyblocks/l/llvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 705cf00557a..24145236d3d 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -1062,6 +1062,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'dirs': check_dirs, } + self._set_gcc_prefix() if lib_dir_runtime: # Required for `clang -v` to work if linked to LLVM runtimes with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)): From e2dab7aff7734436355b3373f08b0a083838ba1a Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 22 Oct 2024 17:45:07 +0200 Subject: [PATCH 071/103] Ensure `zlib` and `zstd` are not used if not provided as deps --- easybuild/easyblocks/l/llvm.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 24145236d3d..dd85030c9f5 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -437,8 +437,10 @@ def _configure_general_build(self): """General configuration step for LLVM.""" self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' - if get_software_root('zlib'): - self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' + # If on risk finding a system zlib or zstd leading to including /usr/include as -isystem that can lead + # to errors during compilation of `offload.tools.kernelreplay`` due to the inclusion of LLVMSupport (19.x) + self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' if get_software_root('zlib') else 'OFF' + self._cmakeopts['LLVM_ENABLE_ZSTD'] = 'ON' if get_software_root('zstd') else 'OFF' z3_root = get_software_root("Z3") if z3_root: @@ -737,22 +739,22 @@ def build_step(self, verbose=False, path=None): else: self.log.info("Building LLVM") print_msg("Building stage 1/1") - change_dir(self.llvm_obj_dir_stage1) - super(EB_LLVM, self).build_step(verbose, path) - # import shutil - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.1', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + # change_dir(self.llvm_obj_dir_stage1) + # super(EB_LLVM, self).build_step(verbose, path) + import shutil + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.1', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - self.configure_step2() - self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.2', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + # self.configure_step2() + # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.2', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") From 6cf5064aaab1f5fad081a1f239ffb5a205b3e38c Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 24 Oct 2024 13:15:44 +0200 Subject: [PATCH 072/103] Revert --- easybuild/easyblocks/l/llvm.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index dd85030c9f5..b4b52134488 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -82,6 +82,7 @@ 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', + 'LIBCXX_HAS_ATOMIC_LIB': 'NO', # Needed as libatomic could not be present on the system 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', 'LIBCXXABI_USE_COMPILER_RT': 'On', @@ -739,22 +740,22 @@ def build_step(self, verbose=False, path=None): else: self.log.info("Building LLVM") print_msg("Building stage 1/1") - # change_dir(self.llvm_obj_dir_stage1) - # super(EB_LLVM, self).build_step(verbose, path) - import shutil - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.1', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVM, self).build_step(verbose, path) + # import shutil + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.1', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - # self.configure_step2() - # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.2', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + self.configure_step2() + self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.2', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") From 88873134192109fd131b5b6d3a25db9f382d826c Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 24 Oct 2024 16:32:00 +0200 Subject: [PATCH 073/103] WIP --- easybuild/easyblocks/l/llvm.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index b4b52134488..389986e81db 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -436,19 +436,6 @@ def _add_cmake_runtime_args(self): def _configure_general_build(self): """General configuration step for LLVM.""" - self._cmakeopts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' - - # If on risk finding a system zlib or zstd leading to including /usr/include as -isystem that can lead - # to errors during compilation of `offload.tools.kernelreplay`` due to the inclusion of LLVMSupport (19.x) - self._cmakeopts['LLVM_ENABLE_ZLIB'] = 'ON' if get_software_root('zlib') else 'OFF' - self._cmakeopts['LLVM_ENABLE_ZSTD'] = 'ON' if get_software_root('zstd') else 'OFF' - - z3_root = get_software_root("Z3") - if z3_root: - self.log.info("Using %s as Z3 root", z3_root) - self._cmakeopts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' - self._cmakeopts['LLVM_Z3_INSTALL_DIR'] = z3_root - self._cmakeopts.update(general_opts) self._add_cmake_runtime_args() @@ -557,6 +544,9 @@ def configure_step(self): self.log.info("Initialising for single stage build.") self.final_dir = self.llvm_obj_dir_stage1 + general_opts['LLVM_ENABLE_ASSERTIONS'] = 'ON' if self.cfg['assertions'] else 'OFF' + + # Dependencies based persistent options (should be reused across stages) # Libxml2 xml2_root = get_software_root('libxml2') if xml2_root: @@ -566,6 +556,20 @@ def configure_step(self): general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' # general_opts['LIBXML2_ROOT'] = xml2_root + + # If `ON`, risk finding a system zlib or zstd leading to including /usr/include as -isystem that can lead + # to errors during compilation of `offload.tools.kernelreplay` due to the inclusion of LLVMSupport (19.x) + general_opts['LLVM_ENABLE_ZLIB'] = 'ON' if get_software_root('zlib') else 'OFF' + general_opts['LLVM_ENABLE_ZSTD'] = 'ON' if get_software_root('zstd') else 'OFF' + # Should not use system SWIG if present + general_opts['LLDB_ENABLE_SWIG'] = 'ON' if get_software_root('SWIG') else 'OFF' + + z3_root = get_software_root("Z3") + if z3_root: + self.log.info("Using %s as Z3 root", z3_root) + general_opts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' + general_opts['LLVM_Z3_INSTALL_DIR'] = z3_root + python_root = get_software_root('Python') if python_root: python_bin = os.path.join(python_root, 'bin', 'python') From 0eb646ebe571eee480cc1bbce706b641d5cd1f87 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 24 Oct 2024 16:50:41 +0200 Subject: [PATCH 074/103] Replace setting python cmake_opts with new functions in CMakeMake --- easybuild/easyblocks/l/llvm.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 389986e81db..b273f47b392 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -52,7 +52,7 @@ X86_64, get_cpu_architecture, get_shared_lib_ext) -from easybuild.easyblocks.generic.cmakemake import CMakeMake +from easybuild.easyblocks.generic.cmakemake import CMakeMake, get_cmake_python_config_dict LLVM_TARGETS = [ 'AArch64', 'AMDGPU', 'ARM', 'AVR', 'BPF', 'Hexagon', 'Lanai', 'LoongArch', 'Mips', 'MSP430', 'NVPTX', 'PowerPC', @@ -82,7 +82,9 @@ 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', - 'LIBCXX_HAS_ATOMIC_LIB': 'NO', # Needed as libatomic could not be present on the system + # Needed as libatomic could not be present on the system (compilation and tests will succeed because of the + # GCCcore builddep, but usage/sanity check will fail due to missing libatomic) + 'LIBCXX_HAS_ATOMIC_LIB': 'NO', 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', 'LIBCXXABI_USE_COMPILER_RT': 'On', @@ -107,7 +109,7 @@ 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', - # Libxml2 from system gets autmatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc + # Libxml2 from system gets automatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc 'LLVM_ENABLE_LIBXML2': 'Off', } @@ -556,7 +558,6 @@ def configure_step(self): general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' # general_opts['LIBXML2_ROOT'] = xml2_root - # If `ON`, risk finding a system zlib or zstd leading to including /usr/include as -isystem that can lead # to errors during compilation of `offload.tools.kernelreplay` due to the inclusion of LLVMSupport (19.x) general_opts['LLVM_ENABLE_ZLIB'] = 'ON' if get_software_root('zlib') else 'OFF' @@ -570,13 +571,9 @@ def configure_step(self): general_opts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' general_opts['LLVM_Z3_INSTALL_DIR'] = z3_root - python_root = get_software_root('Python') - if python_root: - python_bin = os.path.join(python_root, 'bin', 'python') - general_opts['Python_EXECUTABLE'] = python_bin - general_opts['Python3_EXECUTABLE'] = python_bin - self.runtimes_cmake_args['Python_EXECUTABLE'] = python_bin - self.runtimes_cmake_args['Python3_EXECUTABLE'] = python_bin + python_opts = get_cmake_python_config_dict() + general_opts.update(python_opts) + self.runtimes_cmake_args.update(python_opts) self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: From 13e7a2277785123f5f8d2895d58800bd42d50be0 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 24 Oct 2024 18:01:31 +0200 Subject: [PATCH 075/103] lint --- easybuild/easyblocks/l/llvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index b273f47b392..c3ea7c601fb 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -82,7 +82,7 @@ 'LIBCXX_USE_COMPILER_RT': 'On', 'LIBCXX_CXX_ABI': 'libcxxabi', 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', - # Needed as libatomic could not be present on the system (compilation and tests will succeed because of the + # Needed as libatomic could not be present on the system (compilation and tests will succeed because of the # GCCcore builddep, but usage/sanity check will fail due to missing libatomic) 'LIBCXX_HAS_ATOMIC_LIB': 'NO', From 015cdd32c13a9d9ead17fb56e6c2aaebe06a2b48 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 25 Oct 2024 13:54:08 +0200 Subject: [PATCH 076/103] Minor adjustments --- easybuild/easyblocks/l/llvm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index c3ea7c601fb..137fcfdeae0 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -314,7 +314,7 @@ def __init__(self, *args, **kwargs): # Other custom options if self.full_llvm: if not self.cfg['bootstrap']: - raise EasyBuildError("Full LLVM build irequires bootstrap build") + raise EasyBuildError("Full LLVM build requires bootstrap build") if not self.cfg['build_lld']: raise EasyBuildError("Full LLVM build requires building lld") if not self.cfg['build_runtimes']: @@ -585,7 +585,7 @@ def configure_step(self): self._configure_final_build() if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: - self.log.debug("Disabling the sanitizer tests") + self.log.info("Disabling the sanitizer tests") self.disable_sanitizer_tests() # Remove python bindings tests causing uncaught exception in the build @@ -610,8 +610,8 @@ def configure_step(self): general_opts['LIBOMPTARGET_DEVICE_ARCHITECTURES'] = '"%s"' % ';'.join(gpu_archs) self._configure_general_build() - self.add_cmake_opts() + super(EB_LLVM, self).configure_step( builddir=self.llvm_obj_dir_stage1, srcdir=os.path.join(self.llvm_src_dir, "llvm") From 95c0bb9a2823729c0265e481f6eccfccdb3a5894 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 25 Oct 2024 16:12:26 +0200 Subject: [PATCH 077/103] Added command sanity check for BOLT --- easybuild/easyblocks/l/llvm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 137fcfdeae0..12f969d630b 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -1021,6 +1021,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F if 'bolt' in self.final_projects: check_bin_files += ['llvm-bolt', 'llvm-boltdiff', 'llvm-bolt-heatmap'] check_lib_files += ['libbolt_rt_instr.a'] + custom_commands += ['llvm-bolt --help'] if 'openmp' in self.final_projects: omp_lib_files = [] omp_lib_files += ['libomp.so', 'libompd.so'] From 41b69205eec1dc4ae522a9c1f1742b43190a8fc9 Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 28 Oct 2024 15:13:43 +0100 Subject: [PATCH 078/103] Ensure cuda/amd offload plugins are build only if requested --- easybuild/easyblocks/l/llvm.py | 41 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 12f969d630b..0c343fe9d3b 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -274,6 +274,7 @@ def __init__(self, *args, **kwargs): 'CMAKE_CXX_FLAGS': [], 'CMAKE_EXE_LINKER_FLAGS': [], } + self.offload_targets = ['host'] # self._added_librt = None # Shared @@ -372,25 +373,27 @@ def __init__(self, *args, **kwargs): self.log.debug("No build targets specified, using default detection") deps = [dep['name'].lower() for dep in self.cfg.dependencies()] arch = get_cpu_architecture() - try: - default_targets = DEFAULT_TARGETS_MAP[arch][:] - # If CUDA is included as a dep, add NVPTX as a target - # There are (old) toolchains with CUDA as part of the toolchain - cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') - if 'cuda' in deps or cuda_toolchain or cuda_cc_list: - default_targets += ['NVPTX'] - self.log.debug("NVPTX enabled by CUDA dependency/cuda_compute_capabilities") - # For AMDGPU support we need ROCR-Runtime and - # ROCT-Thunk-Interface, however, since ROCT is a dependency of - # ROCR we only check for the ROCR-Runtime here - # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler - if 'rocr-runtime' in deps or amd_gfx_list: - default_targets += ['AMDGPU'] - self.log.debug("AMDGPU enabled by rocr-runtime dependency/amd_gfx_list") - self.cfg['build_targets'] = build_targets = default_targets - self.log.debug("Using %s as default build targets for CPU architecture %s.", default_targets, arch) - except KeyError: + if arch not in DEFAULT_TARGETS_MAP: raise EasyBuildError("No default build targets defined for CPU architecture %s.", arch) + build_targets += DEFAULT_TARGETS_MAP[arch] + + # If CUDA is included as a dep, add NVPTX as a target + # There are (old) toolchains with CUDA as part of the toolchain + cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') + if 'cuda' in deps or cuda_toolchain or cuda_cc_list: + build_targets += ['NVPTX'] + self.offload_targets += ['cuda'] # Used for LLVM >= 19 + self.log.debug("NVPTX enabled by CUDA dependency/cuda_compute_capabilities") + # For AMDGPU support we need ROCR-Runtime and + # ROCT-Thunk-Interface, however, since ROCT is a dependency of + # ROCR we only check for the ROCR-Runtime here + # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler + if 'rocr-runtime' in deps or amd_gfx_list: + build_targets += ['AMDGPU'] + self.offload_targets += ['amdgpu'] # Used for LLVM >= 19 + self.log.debug("AMDGPU enabled by rocr-runtime dependency/amd_gfx_list") + self.cfg['build_targets'] = build_targets + self.log.debug("Using %s as default build targets for CPU architecture %s.", build_targets, arch) unknown_targets = set(build_targets) - set(ALL_TARGETS) @@ -458,6 +461,8 @@ def _configure_final_build(self): self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root if 'openmp' in self.final_projects: + if LooseVersion(self.version) >= LooseVersion('19'): + self._cmakeopts['LIBOMPTARGET_PLUGINS_TO_BUILD'] = ';'.join(self.offload_targets) self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' if not self.cfg['build_openmp_tools']: From e00e92d9870669a3ae83778bf102cba4af351efb Mon Sep 17 00:00:00 2001 From: crivella Date: Mon, 18 Nov 2024 09:55:23 +0100 Subject: [PATCH 079/103] Ensure Z3 is OFF if not a dep --- easybuild/easyblocks/l/llvm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 0c343fe9d3b..8fd2e2b455a 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -575,6 +575,8 @@ def configure_step(self): self.log.info("Using %s as Z3 root", z3_root) general_opts['LLVM_ENABLE_Z3_SOLVER'] = 'ON' general_opts['LLVM_Z3_INSTALL_DIR'] = z3_root + else: + general_opts['LLVM_ENABLE_Z3_SOLVER'] = 'OFF' python_opts = get_cmake_python_config_dict() general_opts.update(python_opts) From a7edd3c3a926df1ac1e9238b5b64b365837709d6 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 7 Feb 2025 16:25:02 +0100 Subject: [PATCH 080/103] Added note on RPATH problem to be solved --- easybuild/easyblocks/l/llvm.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 8fd2e2b455a..70e38c2276c 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -669,6 +669,21 @@ def build_with_prev_stage(self, prev_dir, stage_dir): os.path.join(prev_dir, lib_dir_runtime), ])) + ####################################################### + # PROBLEM!!!: + # Binaries and libraries produced during runtimes make use of the newly built Clang compiler which is not + # rpath-wrapped. This causes the executable to be produced without rpath (if required) and with + # runpath set to $ORIGIN. This causes 2 problems: + # - Binaries produced for the runtimes will fail the sanity check + # - Runtimes libraries that link to libLLVM.so like `libomptarget.so` need LD_LIBRARY_PATH to work. + # This is because even if an executable compiled with the new llvm has rpath pointing to $EBROOTLLVM/lib, + # it will not be resolved with the executable's rpath, but the library's runpath (rpath is ignored if runpath is set). + # Even if libLLVM.so is a direct dependency of the executable, it needs to be resolved both for the executable + # and the library. + # + # Is this true for every runtime?? For now it only seems to be a problem with libomptarget.so and llvm-omp-kernel-replay ?? + ################################################# + # Needed for passing the variables to the build command with _wrap_env(bin_dir, lib_path): # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 From c32ce0d39622548ea77d32e9ae1f570fce829221 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 7 Feb 2025 16:26:09 +0100 Subject: [PATCH 081/103] pic is not anymore an option for system toolchain --- easybuild/easyblocks/l/llvm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 70e38c2276c..7118113b1f2 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -224,6 +224,7 @@ class EB_LLVM(CMakeMake): def extra_options(): extra_vars = CMakeMake.extra_options() extra_vars.update({ + 'use_pic': [True, "Build with Position Independent Code (PIC)", CUSTOM], 'amd_gfx_list': [None, "List of AMDGPU targets to build for. Possible values: " + ', '.join(AMDGPU_GFX_SUPPORT), CUSTOM], 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], @@ -419,7 +420,8 @@ def __init__(self, *args, **kwargs): print_warning("`amd_gfx` specified, but AMDGPU not in manually specified build targets.") general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir - if self.toolchain.options['pic']: + # if self.toolchain.options['pic']: + if self.cfg['use_pic']: general_opts['CMAKE_POSITION_INDEPENDENT_CODE'] = 'ON' general_opts['LLVM_TARGETS_TO_BUILD'] = '"%s"' % ';'.join(build_targets) From 6f520b0a8e6226d176fc8b230ab49ad1bf49bcf4 Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 7 Feb 2025 16:26:19 +0100 Subject: [PATCH 082/103] Imporved sanity_check logic --- easybuild/easyblocks/l/llvm.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 7118113b1f2..93ff9cccfc6 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -941,6 +941,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_files = [] check_bin_files = [] check_lib_files = [] + check_librt_files = [] check_inc_files = [] check_dirs = ['include/llvm', 'include/llvm-c', 'lib/cmake/llvm'] custom_commands = [ @@ -1017,19 +1018,19 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F # ]] # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] if 'libunwind' in self.final_runtimes: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.a']] + check_librt_files += ['libunwind.a'] if self.build_shared: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libunwind.so']] + check_librt_files += ['libunwind.so'] check_inc_files += ['unwind.h', 'libunwind.h', 'mach-o/compact_unwind_encoding.h'] if 'libcxx' in self.final_runtimes: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.a']] + check_librt_files += ['libc++.a'] if self.build_shared: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++.so']] + check_librt_files += ['libc++.so'] check_dirs += ['include/c++/v1'] if 'libcxxabi' in self.final_runtimes: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.a']] + check_librt_files += ['libc++abi.a'] if self.build_shared: - check_files += [os.path.join(lib_dir_runtime, _) for _ in ['libc++abi.so']] + check_librt_files += ['libc++abi.so'] if 'polly' in self.final_projects: check_lib_files += ['libPolly.a', 'libPollyISL.a'] @@ -1053,7 +1054,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F # as static libraries and linked into the libomptarget.so shared library omp_lib_files += ['libomptarget.so'] if LooseVersion(self.version) < LooseVersion('19'): - ['libomptarget.rtl.%s.so' % arch] + omp_lib_files += ['libomptarget.rtl.%s.so' % arch] if 'NVPTX' in self.cfg['build_targets']: if LooseVersion(self.version) < LooseVersion('19'): omp_lib_files += ['libomptarget.rtl.cuda.so'] @@ -1068,10 +1069,14 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_lib_files += omp_lib_files else: # Starting from LLVM 19, omp related libraries are installed the runtime library directory - check_files += [os.path.join(lib_dir_runtime, _) for _ in omp_lib_files] + check_librt_files += omp_lib_files if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] + check_librt_files += ['libarcher.so'] + # In LLVM >= 19 OpenMP tools are build in different places, the purely openmp ones are built as projects + # like `libarcher.so` while other related to offload are built as runtimes + check_bin_files += ['llvm-omp-kernel-replay', 'llvm-omp-device-info'] if self.cfg['python_bindings']: custom_commands += ["python -c 'import clang'"] custom_commands += ["python -c 'import mlir'"] @@ -1084,6 +1089,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_files += [os.path.join('bin', _) for _ in check_bin_files] check_files += [os.path.join('lib', _) for _ in check_lib_files] + check_files += [os.path.join(lib_dir_runtime, _) for _ in check_librt_files] check_files += [os.path.join('include', _) for _ in check_inc_files] custom_paths = { From c2b7c9dd0413b3dba50625666df8de4b2f0b7c8d Mon Sep 17 00:00:00 2001 From: crivella Date: Fri, 7 Feb 2025 17:31:35 +0100 Subject: [PATCH 083/103] WIP: attempt to solve RPATH problem --- easybuild/easyblocks/l/llvm.py | 50 ++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 93ff9cccfc6..e8105de5d4f 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -45,7 +45,7 @@ from easybuild.tools.config import build_option from easybuild.tools.environment import setvar from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, - copy_dir, mkdir, symlink, which) + copy_dir, mkdir, symlink, which, copy_file, remove_file) from easybuild.tools.modules import get_software_root, get_software_version from easybuild.tools.run import run_cmd from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, @@ -271,6 +271,8 @@ def __init__(self, *args, **kwargs): self.final_runtimes = [] self.gcc_prefix = None self.runtimes_cmake_args = { + 'CMAKE_C_COMPILER': [], + 'CMAKE_CXX_COMPILER': [], 'CMAKE_C_FLAGS': [], 'CMAKE_CXX_FLAGS': [], 'CMAKE_EXE_LINKER_FLAGS': [], @@ -679,12 +681,50 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # - Binaries produced for the runtimes will fail the sanity check # - Runtimes libraries that link to libLLVM.so like `libomptarget.so` need LD_LIBRARY_PATH to work. # This is because even if an executable compiled with the new llvm has rpath pointing to $EBROOTLLVM/lib, - # it will not be resolved with the executable's rpath, but the library's runpath (rpath is ignored if runpath is set). - # Even if libLLVM.so is a direct dependency of the executable, it needs to be resolved both for the executable - # and the library. + # it will not be resolved with the executable's rpath, but the library's runpath + # (rpath is ignored if runpath is set). + # Even if libLLVM.so is a direct dependency of the executable, it needs to be resolved both for the + # executable and the library. # - # Is this true for every runtime?? For now it only seems to be a problem with libomptarget.so and llvm-omp-kernel-replay ?? + # Here we create a mock binary for the current stage by copying the previous one, rpath-wrapping it and + # and than pass the rpath-wrapped binary to the runtimes build as the compiler. ################################################# + bin_dir_new = os.path.join(stage_dir, 'bin') + with _wrap_env(bin_dir_new, lib_path): + if build_option('rpath'): + prev_clang = os.path.join(bin_dir, 'clang') + prev_clangxx = os.path.join(bin_dir, 'clang++') + nxt_clang = os.path.join(bin_dir_new, 'clang') + nxt_clangxx = os.path.join(bin_dir_new, 'clang++') + copy_file(prev_clang, nxt_clang) + copy_file(prev_clangxx, nxt_clangxx) + + tmp_toolchain = Clang(name='Clang', version='1') + tmp_toolchain.prepare_rpath_wrappers( + rpath_include_dirs=[ + # Don't need stage dir here as LD_LIBRARY_PATH is set during build, this is only needed for + # installed binaries with rpath + os.path.join(self.installdir, 'lib'), + os.path.join(self.installdir, 'lib64'), + os.path.join(self.installdir, lib_dir_runtime), + ] + ) + remove_file(nxt_clang) + remove_file(nxt_clangxx) + self.log.info( + "Prepared MOCK rpath wrappers needed to rpath-wrap also the new compilers produced " + "by the project build and than used for the runtimes build" + ) + clang_mock = which('clang') + clangxx_mock = which('clang++') + + clang_mock_wrapper_dir = os.path.dirname(clang_mock) + + symlink(os.path.join(stage_dir, 'opt'), os.path.join(clang_mock_wrapper_dir, 'opt')) + + self.runtimes_cmake_args['CMAKE_C_COMPILER'] = [clang_mock] + self.runtimes_cmake_args['CMAKE_CXX_COMPILER'] = [clangxx_mock] + # Needed for passing the variables to the build command with _wrap_env(bin_dir, lib_path): From 8563044803a440a8e4c191e25e0cf8d30ed83c20 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 11 Feb 2025 12:38:41 +0100 Subject: [PATCH 084/103] Converted from `run_cmd` to `run_shell_cmd` --- easybuild/easyblocks/l/llvm.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index e8105de5d4f..7815522c0ca 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -47,7 +47,7 @@ from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, copy_dir, mkdir, symlink, which, copy_file, remove_file) from easybuild.tools.modules import get_software_root, get_software_version -from easybuild.tools.run import run_cmd +from easybuild.tools.run import run_cmd, run_shell_cmd from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, X86_64, get_cpu_architecture, get_shared_lib_ext) @@ -188,7 +188,8 @@ def sanity_check_gcc_prefix(compilers, gcc_prefix, installdir): rgx = re.compile('Selected GCC installation: (.*)') for comp in compilers: cmd = "%s -v" % os.path.join(installdir, 'bin', comp) - out, _ = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + res = run_shell_cmd(cmd, fail_on_error=False) + out = res.output mch = rgx.search(out) if mch is None: raise EasyBuildError("Failed to extract GCC installation path from output of `%s`: %s", cmd, out) @@ -789,11 +790,11 @@ def build_with_prev_stage(self, prev_dir, stage_dir): change_dir(stage_dir) self.log.debug("Configuring %s", stage_dir) cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) - run_cmd(cmd, log_all=True) + run_shell_cmd(cmd) self.log.debug("Building %s", stage_dir) cmd = "make %s VERBOSE=1" % self.make_parallel_opts - run_cmd(cmd, log_all=True) + run_shell_cmd(cmd) change_dir(curdir) @@ -842,7 +843,8 @@ def _para_test_step(self, parallel=1): lib_path = os.path.join(basedir, lib_dir_runtime) with _wrap_env(os.path.join(basedir, 'bin'), lib_path): cmd = "make -j %s check-all" % parallel - (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False) + res = run_shell_cmd(cmd, fail_on_error=False) + out = res.output self.log.debug(out) rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) From 6185501827b3b46caa2fff2cbdcb8ec24d88cf7c Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 11 Feb 2025 12:40:29 +0100 Subject: [PATCH 085/103] Option to enable/disable build of `offload` --- easybuild/easyblocks/l/llvm.py | 46 ++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 7815522c0ca..20b2cce3cfa 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -244,6 +244,7 @@ def extra_options(): 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], 'build_runtimes': [False, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], + 'build_openmp_offload': [True, "Build the LLVM OpenMP offload runtime", CUSTOM], 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], 'usepolly': [False, "Build Clang with polly", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], @@ -332,11 +333,16 @@ def __init__(self, *args, **kwargs): self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] if self.cfg['build_openmp']: self.final_projects.append('openmp') + if self.cfg['build_openmp_offload']: + if not self.cfg['build_openmp']: + raise EasyBuildError("Building OpenMP offload requires building OpenMP runtime") # LLVM 19 added a new runtime target for explicit offloading # https://discourse.llvm.org/t/llvm-19-1-0-no-library-libomptarget-nvptx-sm-80-bc-found/81343 if LooseVersion(self.version) >= LooseVersion('19'): self.log.debug("Explicitly enabling OpenMP offloading for LLVM >= 19") self.final_runtimes.append('offload') + else: + self.log.warning("OpenMP offloading is included with the OpenMP runtime for LLVM < 19") if self.cfg['build_openmp_tools']: if not self.cfg['build_openmp']: raise EasyBuildError("Building OpenMP tools requires building OpenMP runtime") @@ -466,7 +472,7 @@ def _configure_final_build(self): self._cmakeopts['LIBOMP_HWLOC_INSTALL_DIR'] = hwloc_root if 'openmp' in self.final_projects: - if LooseVersion(self.version) >= LooseVersion('19'): + if LooseVersion(self.version) >= LooseVersion('19') and self.cfg['build_openmp_offload']: self._cmakeopts['LIBOMPTARGET_PLUGINS_TO_BUILD'] = ';'.join(self.offload_targets) self._cmakeopts['OPENMP_ENABLE_LIBOMPTARGET'] = 'ON' self._cmakeopts['LIBOMP_INSTALL_ALIASES'] = 'OFF' @@ -1092,26 +1098,28 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F if 'openmp' in self.final_projects: omp_lib_files = [] omp_lib_files += ['libomp.so', 'libompd.so'] - # Judging from the build process/logs of LLVM 19, the omptarget plugins (rtl..so) are now built - # as static libraries and linked into the libomptarget.so shared library - omp_lib_files += ['libomptarget.so'] - if LooseVersion(self.version) < LooseVersion('19'): - omp_lib_files += ['libomptarget.rtl.%s.so' % arch] - if 'NVPTX' in self.cfg['build_targets']: + if self.cfg['build_openmp_offload']: + # Judging from the build process/logs of LLVM 19, the omptarget plugins (rtl..so) are now built + # as static libraries and linked into the libomptarget.so shared library + omp_lib_files += ['libomptarget.so'] if LooseVersion(self.version) < LooseVersion('19'): - omp_lib_files += ['libomptarget.rtl.cuda.so'] - omp_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] - if 'AMDGPU' in self.cfg['build_targets']: - if LooseVersion(self.version) < LooseVersion('19'): - omp_lib_files += ['libomptarget.rtl.amdgpu.so'] - omp_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] + omp_lib_files += ['libomptarget.rtl.%s.so' % arch] + if 'NVPTX' in self.cfg['build_targets']: + if LooseVersion(self.version) < LooseVersion('19'): + omp_lib_files += ['libomptarget.rtl.cuda.so'] + omp_lib_files += ['libomptarget-nvptx-sm_%s.bc' % cc for cc in self.cuda_cc] + if 'AMDGPU' in self.cfg['build_targets']: + if LooseVersion(self.version) < LooseVersion('19'): + omp_lib_files += ['libomptarget.rtl.amdgpu.so'] + omp_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] - if LooseVersion(self.version) < LooseVersion('19'): - # Before LLVM 19, omp related libraries are installed under `ROOT/lib`` - check_lib_files += omp_lib_files - else: - # Starting from LLVM 19, omp related libraries are installed the runtime library directory - check_librt_files += omp_lib_files + if LooseVersion(self.version) < LooseVersion('19'): + # Before LLVM 19, omp related libraries are installed under `ROOT/lib`` + check_lib_files += omp_lib_files + else: + # Starting from LLVM 19, omp related libraries are installed the runtime library directory + check_librt_files += omp_lib_files + check_bin_files += ['llvm-omp-kernel-replay', 'llvm-omp-device-info'] if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] From a7d6d8e79b8efe52821cb2f71e9306cafdc4f88a Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 11 Feb 2025 12:41:47 +0100 Subject: [PATCH 086/103] Fix --- easybuild/easyblocks/l/llvm.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 20b2cce3cfa..4a7472e4d4e 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -1124,9 +1124,6 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] check_librt_files += ['libarcher.so'] - # In LLVM >= 19 OpenMP tools are build in different places, the purely openmp ones are built as projects - # like `libarcher.so` while other related to offload are built as runtimes - check_bin_files += ['llvm-omp-kernel-replay', 'llvm-omp-device-info'] if self.cfg['python_bindings']: custom_commands += ["python -c 'import clang'"] custom_commands += ["python -c 'import mlir'"] From 906dffcc372fe67af89f511f4596de0f19ce3417 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 11 Feb 2025 12:44:27 +0100 Subject: [PATCH 087/103] Removed check for `clang-pseudo` --- easybuild/easyblocks/l/llvm.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 4a7472e4d4e..192afed7fac 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -812,22 +812,22 @@ def build_step(self, verbose=False, path=None): else: self.log.info("Building LLVM") print_msg("Building stage 1/1") - change_dir(self.llvm_obj_dir_stage1) - super(EB_LLVM, self).build_step(verbose, path) - # import shutil - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.1', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + # change_dir(self.llvm_obj_dir_stage1) + # super(EB_LLVM, self).build_step(verbose, path) + import shutil + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.1', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - self.configure_step2() - self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.2', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + # self.configure_step2() + # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + change_dir(self.builddir) + print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + shutil.rmtree('llvm.obj.2', ignore_errors=True) + shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") @@ -1016,8 +1016,9 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F gcc_prefix_compilers += ['clang', 'clang++'] if 'clang-tools-extra' in self.final_projects: + # clang-pseudo removed with LLVM 20 check_bin_files += [ - 'clangd', 'clang-tidy', 'clang-pseudo', 'clang-include-fixer', 'clang-query', 'clang-move', + 'clangd', 'clang-tidy', 'clang-include-fixer', 'clang-query', 'clang-move', 'clang-reorder-fields', 'clang-include-cleaner', 'clang-apply-replacements', 'clang-change-namespace', 'pp-trace', 'modularize' ] From 52ccbc1ce280c9f62bb110307398065dab6cc4ce Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 13 Feb 2025 14:28:30 +0100 Subject: [PATCH 088/103] lint --- easybuild/easyblocks/l/llvm.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 192afed7fac..7d50d5f7702 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -47,7 +47,7 @@ from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, copy_dir, mkdir, symlink, which, copy_file, remove_file) from easybuild.tools.modules import get_software_root, get_software_version -from easybuild.tools.run import run_cmd, run_shell_cmd +from easybuild.tools.run import run_shell_cmd from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, X86_64, get_cpu_architecture, get_shared_lib_ext) @@ -681,9 +681,9 @@ def build_with_prev_stage(self, prev_dir, stage_dir): ])) ####################################################### - # PROBLEM!!!: + # PROBLEM!!!: # Binaries and libraries produced during runtimes make use of the newly built Clang compiler which is not - # rpath-wrapped. This causes the executable to be produced without rpath (if required) and with + # rpath-wrapped. This causes the executable to be produced without rpath (if required) and with # runpath set to $ORIGIN. This causes 2 problems: # - Binaries produced for the runtimes will fail the sanity check # - Runtimes libraries that link to libLLVM.so like `libomptarget.so` need LD_LIBRARY_PATH to work. @@ -732,7 +732,6 @@ def build_with_prev_stage(self, prev_dir, stage_dir): self.runtimes_cmake_args['CMAKE_C_COMPILER'] = [clang_mock] self.runtimes_cmake_args['CMAKE_CXX_COMPILER'] = [clangxx_mock] - # Needed for passing the variables to the build command with _wrap_env(bin_dir, lib_path): # If building with rpath, create RPATH wrappers for the Clang compilers for stage 2 and 3 From 6b03e9852db7f1c1956e876c63424310ed29111b Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 13 Feb 2025 14:39:03 +0100 Subject: [PATCH 089/103] Fix for possible different recognized triples --- easybuild/easyblocks/l/llvm.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 7d50d5f7702..33c17b0cceb 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -219,6 +219,11 @@ class EB_LLVM(CMakeMake): 'usepolly', ] + symlink_lst = [ + ('x86_64-unknown-linux-gnu', 'x86_64-pc-linux'), + ('x86_64-unknown-linux-gnu', 'x86_64-pc-linux-gnu'), + ] + cfg_compilers = ['clang', 'clang++', 'flang'] @staticmethod @@ -938,6 +943,22 @@ def post_install_step(self): self._set_gcc_prefix() create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.installdir) + # This is needed as some older build system will select a different naming scheme for the library leading to + # The correct target <__config_site> and libclang_rt.builtins.a not being found + # An example is building BOOST + resdir_version = self.version.split('.')[0] + clang_lib = os.path.join(self.installdir, 'lib', 'clang', resdir_version, 'lib') + + for orig, other in self.symlink_lst: + for dirname in ['include', 'lib', clang_lib]: + src = os.path.join(self.installdir, dirname, orig) + dst = os.path.join(self.installdir, dirname, other) + if os.path.exists(src) and not os.path.exists(dst): + self.log.info( + "Creating symlink for %s to %s for better compatibility with expected --target", src, dst + ) + symlink(src, dst) + def get_runtime_lib_path(self, base_dir, fail_ok=True): """Return the path to the runtime libraries.""" arch = get_cpu_architecture() From a748b75c3c9d6755c3813ae9842509876c87882e Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 13 Feb 2025 15:03:30 +0100 Subject: [PATCH 090/103] Added clarifying comments --- easybuild/easyblocks/l/llvm.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 33c17b0cceb..bcb352e3f1c 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -219,11 +219,17 @@ class EB_LLVM(CMakeMake): 'usepolly', ] + # Create symlink between equivalent host triples, useful so that other build processes that relies on older + # triple names can still work when passing the old name to --target symlink_lst = [ ('x86_64-unknown-linux-gnu', 'x86_64-pc-linux'), ('x86_64-unknown-linux-gnu', 'x86_64-pc-linux-gnu'), ] + # From LLVM19, GCC_INSTALL_PREFIX is not supported anymore to hardcode the GCC installation path into the binaries + # Now every compilers needs a .cfg file with the --gcc-install-dir option + # This list tells which compilers need to have a .cfg file created + # NOTE: flang.cfg is the expected name also for the `flang-new` compiler cfg_compilers = ['clang', 'clang++', 'flang'] @staticmethod From f94c2e3a2bbbc7cc8e3db15e8ca7e8d90e5d0d3d Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Tue, 4 Mar 2025 07:33:22 +0100 Subject: [PATCH 091/103] import ERROR from tools.config rather than assuming it's available in tools.run --- easybuild/easyblocks/l/llvm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index bcb352e3f1c..3369575d3a4 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -42,7 +42,7 @@ from easybuild.toolchains.compiler.clang import Clang from easybuild.tools import LooseVersion, run from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning -from easybuild.tools.config import build_option +from easybuild.tools.config import ERROR, build_option from easybuild.tools.environment import setvar from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, copy_dir, mkdir, symlink, which, copy_file, remove_file) @@ -613,7 +613,7 @@ def configure_step(self): else: self._configure_final_build() - if self.cfg['skip_sanitizer_tests'] and build_option('strict') != run.ERROR: + if self.cfg['skip_sanitizer_tests'] and build_option('strict') != ERROR: self.log.info("Disabling the sanitizer tests") self.disable_sanitizer_tests() From 7d956dc7c8d63a086f2495bcbc5328e9bbbb7577 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 4 Mar 2025 09:18:12 +0100 Subject: [PATCH 092/103] Revert to proper build without copies --- easybuild/easyblocks/l/llvm.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index bcb352e3f1c..c8290104154 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -822,22 +822,22 @@ def build_step(self, verbose=False, path=None): else: self.log.info("Building LLVM") print_msg("Building stage 1/1") - # change_dir(self.llvm_obj_dir_stage1) - # super(EB_LLVM, self).build_step(verbose, path) - import shutil - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.1', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') + change_dir(self.llvm_obj_dir_stage1) + super(EB_LLVM, self).build_step(verbose, path) + # import shutil + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.1', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") - # self.configure_step2() - # self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - change_dir(self.builddir) - print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - shutil.rmtree('llvm.obj.2', ignore_errors=True) - shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') + self.configure_step2() + self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) + # change_dir(self.builddir) + # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") + # shutil.rmtree('llvm.obj.2', ignore_errors=True) + # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") From 1646380180c2dd01d05e8f1582521e0953433524 Mon Sep 17 00:00:00 2001 From: crivella Date: Tue, 4 Mar 2025 09:19:04 +0100 Subject: [PATCH 093/103] Also removed comments --- easybuild/easyblocks/l/llvm.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index c8290104154..4fe586e3b36 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -824,29 +824,16 @@ def build_step(self, verbose=False, path=None): print_msg("Building stage 1/1") change_dir(self.llvm_obj_dir_stage1) super(EB_LLVM, self).build_step(verbose, path) - # import shutil - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.1', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.1'), 'llvm.obj.1') if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") self.configure_step2() self.build_with_prev_stage(self.llvm_obj_dir_stage1, self.llvm_obj_dir_stage2) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.2', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.2'), 'llvm.obj.2') self.log.info("Building stage 3") print_msg("Building stage 3/3") self.configure_step3() self.build_with_prev_stage(self.llvm_obj_dir_stage2, self.llvm_obj_dir_stage3) - # change_dir(self.builddir) - # print_msg("TESTING!!!: Copying from previosu build (REMOVE ME)") - # shutil.rmtree('llvm.obj.3', ignore_errors=True) - # shutil.copytree(os.path.join('..', 'llvm.obj.3'), 'llvm.obj.3') def _para_test_step(self, parallel=1): """Run test suite with the specified number of parallel jobs for make.""" From 10674ad00959416118d21cde58d17270caac398e Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Tue, 4 Mar 2025 19:52:22 +0100 Subject: [PATCH 094/103] update LLVM easyblock to not rely on deprecated functionality in EasyBuild 5.x, move function to 'private' static methods in LLVM easyblock class + minor code cleanup --- easybuild/easyblocks/l/llvm.py | 468 ++++++++++++++++----------------- 1 file changed, 228 insertions(+), 240 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index c83cde3adfa..7bff2537328 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -40,23 +40,25 @@ from easybuild.framework.easyconfig import CUSTOM from easybuild.toolchains.compiler.clang import Clang -from easybuild.tools import LooseVersion, run +from easybuild.tools import LooseVersion from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning -from easybuild.tools.config import ERROR, build_option +from easybuild.tools.config import ERROR, SEARCH_PATH_LIB_DIRS, build_option from easybuild.tools.environment import setvar -from easybuild.tools.filetools import (apply_regex_substitutions, change_dir, - copy_dir, mkdir, symlink, which, copy_file, remove_file) -from easybuild.tools.modules import get_software_root, get_software_version +from easybuild.tools.filetools import apply_regex_substitutions, change_dir, copy_dir, copy_file +from easybuild.tools.filetools import mkdir, remove_file, symlink, which, write_file +from easybuild.tools.modules import MODULE_LOAD_ENV_HEADERS, get_software_root, get_software_version from easybuild.tools.run import run_shell_cmd -from easybuild.tools.systemtools import (AARCH32, AARCH64, POWER, RISCV64, - X86_64, get_cpu_architecture, - get_shared_lib_ext) +from easybuild.tools.systemtools import AARCH32, AARCH64, POWER, RISCV64, X86_64 +from easybuild.tools.systemtools import get_cpu_architecture, get_shared_lib_ext from easybuild.easyblocks.generic.cmakemake import CMakeMake, get_cmake_python_config_dict +BUILD_TARGET_AMDGPU = 'AMDGPU' +BUILD_TARGET_NVPTX = 'NVPTX' + LLVM_TARGETS = [ - 'AArch64', 'AMDGPU', 'ARM', 'AVR', 'BPF', 'Hexagon', 'Lanai', 'LoongArch', 'Mips', 'MSP430', 'NVPTX', 'PowerPC', - 'RISCV', 'Sparc', 'SystemZ', 'VE', 'WebAssembly', 'X86', 'XCore', + 'AArch64', BUILD_TARGET_AMDGPU, 'ARM', 'AVR', 'BPF', 'Hexagon', 'Lanai', 'LoongArch', 'Mips', 'MSP430', + BUILD_TARGET_NVPTX, 'PowerPC', 'RISCV', 'Sparc', 'SystemZ', 'VE', 'WebAssembly', 'X86', 'XCore', 'all' ] LLVM_EXPERIMENTAL_TARGETS = [ @@ -79,63 +81,63 @@ ] remove_gcc_dependency_opts = { - 'LIBCXX_USE_COMPILER_RT': 'On', + 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', + 'CLANG_DEFAULT_RTLIB': 'compiler-rt', + # Moved to general_opts for ease of building with openmp offload (or other multi-stage builds) + # 'CLANG_DEFAULT_LINKER': 'lld', + 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', + + 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', + 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', + 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html + 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', + 'COMPILER_RT_USE_LIBCXX': 'On', + 'COMPILER_RT_USE_LLVM_UNWINDER': 'On', + 'LIBCXX_CXX_ABI': 'libcxxabi', 'LIBCXX_DEFAULT_ABI_LIBRARY': 'libcxxabi', # Needed as libatomic could not be present on the system (compilation and tests will succeed because of the # GCCcore builddep, but usage/sanity check will fail due to missing libatomic) 'LIBCXX_HAS_ATOMIC_LIB': 'NO', + 'LIBCXX_HAS_GCC_S_LIB': 'Off', + 'LIBCXX_USE_COMPILER_RT': 'On', + 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', 'LIBCXXABI_USE_LLVM_UNWINDER': 'On', 'LIBCXXABI_USE_COMPILER_RT': 'On', - 'LIBUNWIND_USE_COMPILER_RT': 'On', - - 'SANITIZER_USE_STATIC_LLVM_UNWINDER': 'On', - 'COMPILER_RT_USE_LIBCXX': 'On', - 'COMPILER_RT_USE_LLVM_UNWINDER': 'On', - 'COMPILER_RT_USE_BUILTINS_LIBRARY': 'On', - 'COMPILER_RT_ENABLE_STATIC_UNWINDER': 'On', # https://lists.llvm.org/pipermail/llvm-bugs/2016-July/048424.html - 'COMPILER_RT_ENABLE_INTERNAL_SYMBOLIZER': 'On', - 'COMPILER_RT_BUILD_GWP_ASAN': 'Off', - - 'CLANG_DEFAULT_CXX_STDLIB': 'libc++', - 'CLANG_DEFAULT_RTLIB': 'compiler-rt', - # Moved to general_opts for ease of building with openmp offload (or other multi-stage builds) - # 'CLANG_DEFAULT_LINKER': 'lld', - 'CLANG_DEFAULT_UNWINDLIB': 'libunwind', - - 'LIBCXX_HAS_GCC_S_LIB': 'Off', - 'LIBCXXABI_HAS_GCC_S_LIB': 'Off', 'LIBUNWIND_HAS_GCC_S_LIB': 'Off', + 'LIBUNWIND_USE_COMPILER_RT': 'On', # Libxml2 from system gets automatically detected and linked in bringing dependencies from stdc++, gcc_s, icuuc, etc 'LLVM_ENABLE_LIBXML2': 'Off', + + 'SANITIZER_USE_STATIC_LLVM_UNWINDER': 'On', } disable_werror = { - 'LLVM_ENABLE_WERROR': 'Off', 'BENCHMARK_ENABLE_WERROR': 'Off', 'COMPILER_RT_ENABLE_WERROR': 'Off', + 'FLANG_ENABLE_WERROR': 'Off', 'LIBC_WNO_ERROR': 'On', 'LIBCXX_ENABLE_WERROR': 'Off', 'LIBUNWIND_ENABLE_WERROR': 'Off', + 'LLVM_ENABLE_WERROR': 'Off', 'OPENMP_ENABLE_WERROR': 'Off', - 'FLANG_ENABLE_WERROR': 'Off', } general_opts = { + 'CMAKE_VERBOSE_MAKEFILE': 'ON', + 'LLVM_INCLUDE_BENCHMARKS': 'OFF', + 'LLVM_INSTALL_UTILS': 'ON', # If EB is launched from a venv, avoid giving priority to the venv's python 'Python3_FIND_VIRTUALENV': 'STANDARD', - 'LLVM_INSTALL_UTILS': 'ON', - 'LLVM_INCLUDE_BENCHMARKS': 'OFF', - 'CMAKE_VERBOSE_MAKEFILE': 'ON', } @contextlib.contextmanager def _wrap_env(path="", ld_path=""): - """Wrap the environment with the path and ld_path.""" + """Wrap the environment with $PATH and $LD_LIBRARY_PATH.""" orig_path = os.getenv('PATH', '') orig_ld_library_path = os.getenv('LD_LIBRARY_PATH', '') @@ -152,70 +154,22 @@ def _wrap_env(path="", ld_path=""): setvar('LD_LIBRARY_PATH', orig_ld_library_path) -def get_gcc_prefix(): - """Get the GCC prefix for the build.""" - arch = get_cpu_architecture() - gcc_root = get_software_root('GCCcore') - gcc_version = get_software_version('GCCcore') - # If that doesn't work, try with GCC - if gcc_root is None: - gcc_root = get_software_root('GCC') - gcc_version = get_software_version('GCC') - # If that doesn't work either, print error and exit - if gcc_root is None: - raise EasyBuildError("Can't find GCC or GCCcore to use") - - pattern = os.path.join(gcc_root, 'lib', 'gcc', '%s-*' % arch, '%s' % gcc_version) - matches = glob.glob(pattern) - if not matches: - raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) - gcc_prefix = os.path.abspath(matches[0]) - - return gcc_root, gcc_prefix - - -def create_compiler_config_file(compilers, gcc_prefix, installdir): - """Create a config file for the compiler to point to the correct GCC installation.""" - bin_dir = os.path.join(installdir, 'bin') - prefix_str = '--gcc-install-dir=%s' % gcc_prefix - for comp in compilers: - with open(os.path.join(bin_dir, '%s.cfg' % comp), 'w') as f: - f.write(prefix_str) - - -def sanity_check_gcc_prefix(compilers, gcc_prefix, installdir): - """Check if the GCC prefix of the compiler is correct""" - rgx = re.compile('Selected GCC installation: (.*)') - for comp in compilers: - cmd = "%s -v" % os.path.join(installdir, 'bin', comp) - res = run_shell_cmd(cmd, fail_on_error=False) - out = res.output - mch = rgx.search(out) - if mch is None: - raise EasyBuildError("Failed to extract GCC installation path from output of `%s`: %s", cmd, out) - check_prefix = mch.group(1) - if check_prefix != gcc_prefix: - raise EasyBuildError( - "GCC installation path `%s` does not match expected path `%s`", check_prefix, gcc_prefix - ) - - class EB_LLVM(CMakeMake): """ Support for building and installing LLVM """ minimal_conflicts = [ - 'bootstrap', - 'full_llvm', - 'python_bindings', - 'build_clang_extras', 'build_bolt', + 'build_clang_extras', 'build_lld', 'build_lldb', - 'build_runtimes', 'build_openmp', 'build_openmp_tools', + 'build_runtimes', + 'bootstrap', + 'full_llvm', + 'python_bindings', 'usepolly', ] @@ -226,43 +180,43 @@ class EB_LLVM(CMakeMake): ('x86_64-unknown-linux-gnu', 'x86_64-pc-linux-gnu'), ] - # From LLVM19, GCC_INSTALL_PREFIX is not supported anymore to hardcode the GCC installation path into the binaries + # From LLVM 19, GCC_INSTALL_PREFIX is not supported anymore to hardcode the GCC installation path into the binaries; # Now every compilers needs a .cfg file with the --gcc-install-dir option # This list tells which compilers need to have a .cfg file created - # NOTE: flang.cfg is the expected name also for the `flang-new` compiler + # NOTE: flang is the expected name also for the 'flang-new' compiler cfg_compilers = ['clang', 'clang++', 'flang'] @staticmethod def extra_options(): extra_vars = CMakeMake.extra_options() extra_vars.update({ - 'use_pic': [True, "Build with Position Independent Code (PIC)", CUSTOM], 'amd_gfx_list': [None, "List of AMDGPU targets to build for. Possible values: " + ', '.join(AMDGPU_GFX_SUPPORT), CUSTOM], 'assertions': [False, "Enable assertions. Helps to catch bugs in Clang.", CUSTOM], - 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + - ', '.join(ALL_TARGETS), CUSTOM], 'bootstrap': [True, "Build LLVM-Clang using itself", CUSTOM], - 'full_llvm': [False, "Build LLVM without any dependency", CUSTOM], - 'minimal': [False, "Build LLVM only", CUSTOM], - 'enable_rtti': [True, "Enable RTTI", CUSTOM], - 'skip_all_tests': [False, "Skip running of tests", CUSTOM], - 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], - 'python_bindings': [False, "Install python bindings", CUSTOM], - 'build_clang_extras': [False, "Build the LLVM Clang extra tools", CUSTOM], 'build_bolt': [False, "Build the LLVM bolt binary optimizer", CUSTOM], + 'build_clang_extras': [False, "Build the LLVM Clang extra tools", CUSTOM], 'build_lld': [False, "Build the LLVM lld linker", CUSTOM], 'build_lldb': [False, "Build the LLVM lldb debugger", CUSTOM], - 'build_runtimes': [False, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], 'build_openmp': [True, "Build the LLVM OpenMP runtime", CUSTOM], 'build_openmp_offload': [True, "Build the LLVM OpenMP offload runtime", CUSTOM], 'build_openmp_tools': [True, "Build the LLVM OpenMP tools interface", CUSTOM], - 'usepolly': [False, "Build Clang with polly", CUSTOM], + 'build_runtimes': [False, "Build the LLVM runtimes (compiler-rt, libunwind, libcxx, libcxxabi)", CUSTOM], + 'build_targets': [None, "Build targets for LLVM (host architecture if None). Possible values: " + + ', '.join(ALL_TARGETS), CUSTOM], + 'debug_tests': [True, "Enable verbose output for tests", CUSTOM], 'disable_werror': [False, "Disable -Werror for all projects", CUSTOM], + 'enable_rtti': [True, "Enable RTTI", CUSTOM], + 'full_llvm': [False, "Build LLVM without any dependency", CUSTOM], + 'minimal': [False, "Build LLVM only", CUSTOM], + 'python_bindings': [False, "Install python bindings", CUSTOM], + 'skip_all_tests': [False, "Skip running of tests", CUSTOM], + 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], - 'test_suite_timeout_total': [None, "Timeout for total running time of the testsuite", CUSTOM], 'test_suite_timeout_single': [None, "Timeout for each individual test in the test suite", CUSTOM], - 'debug_tests': [True, "Enable verbose output for tests", CUSTOM], + 'test_suite_timeout_total': [None, "Timeout for total running time of the testsuite", CUSTOM], + 'use_pic': [True, "Build with Position Independent Code (PIC)", CUSTOM], + 'usepolly': [False, "Build Clang with polly", CUSTOM], }) return extra_vars @@ -285,8 +239,8 @@ def __init__(self, *args, **kwargs): self.gcc_prefix = None self.runtimes_cmake_args = { 'CMAKE_C_COMPILER': [], - 'CMAKE_CXX_COMPILER': [], 'CMAKE_C_FLAGS': [], + 'CMAKE_CXX_COMPILER': [], 'CMAKE_CXX_FLAGS': [], 'CMAKE_EXE_LINKER_FLAGS': [], } @@ -294,39 +248,39 @@ def __init__(self, *args, **kwargs): # self._added_librt = None # Shared + off_opts, on_opts = [], [] self.build_shared = self.cfg.get('build_shared_libs', False) if self.build_shared: self.cfg['build_shared_libs'] = None - general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'ON' - general_opts['LLVM_LINK_LLVM_DYLIB'] = 'ON' - general_opts['LIBCXX_ENABLE_SHARED'] = 'ON' - general_opts['LIBCXXABI_ENABLE_SHARED'] = 'ON' - general_opts['LIBUNWIND_ENABLE_SHARED'] = 'ON' + on_opts.extend(['LLVM_BUILD_LLVM_DYLIB', 'LLVM_LINK_LLVM_DYLIB', 'LIBCXX_ENABLE_SHARED', + 'LIBCXXABI_ENABLE_SHARED', 'LIBUNWIND_ENABLE_SHARED']) else: - general_opts['LLVM_BUILD_LLVM_DYLIB'] = 'OFF' - general_opts['LLVM_LINK_LLVM_DYLIB'] = 'OFF' - general_opts['LIBCXX_ENABLE_SHARED'] = 'OFF' - general_opts['LIBCXXABI_ENABLE_SHARED'] = 'OFF' - general_opts['LIBUNWIND_ENABLE_SHARED'] = 'OFF' - general_opts['LIBCXX_ENABLE_STATIC'] = 'ON' - general_opts['LIBCXX_ENABLE_STATIC_ABI_LIBRARY'] = 'ON' - general_opts['LIBCXX_ENABLE_ABI_LINKER_SCRIPT'] = 'OFF' - general_opts['LIBCXXABI_ENABLE_STATIC'] = 'ON' - general_opts['LIBUNWIND_ENABLE_STATIC'] = 'ON' + off_opts.extend(['LIBCXX_ENABLE_ABI_LINKER_SCRIPT', 'LIBCXX_ENABLE_SHARED', 'LIBCXXABI_ENABLE_SHARED', + 'LIBUNWIND_ENABLE_SHARED', 'LLVM_BUILD_LLVM_DYLIB', 'LLVM_LINK_LLVM_DYLIB']) + on_opts.extend(['LIBCXX_ENABLE_STATIC', 'LIBCXX_ENABLE_STATIC_ABI_LIBRARY', 'LIBCXXABI_ENABLE_STATIC', + 'LIBUNWIND_ENABLE_STATIC']) # RTTI - if self.cfg["enable_rtti"]: - general_opts['LLVM_REQUIRES_RTTI'] = 'ON' - general_opts['LLVM_ENABLE_RTTI'] = 'ON' - # Does not work with Flang - # general_opts['LLVM_ENABLE_EH'] = 'ON' + if self.cfg['enable_rtti']: + on_opts.extend(['LLVM_ENABLE_RTTI', 'LLVM_REQUIRES_RTTI']) + # Does not work yet with Flang + # on_opts.append('LLVM_ENABLE_EH') + + if self.cfg['use_pic']: + on_opts.append('CMAKE_POSITION_INDEPENDENT_CODE') + + for opt in on_opts: + general_opts[opt] = 'ON' + + for opt in off_opts: + general_opts[opt] = 'OFF' self.full_llvm = self.cfg['full_llvm'] if self.cfg['minimal']: conflicts = [_ for _ in self.minimal_conflicts if self.cfg[_]] if conflicts: - raise EasyBuildError("Minimal build conflicts with `%s`", ', '.join(conflicts)) + raise EasyBuildError("Minimal build conflicts with '%s'", ', '.join(conflicts)) # Other custom options if self.full_llvm: @@ -340,10 +294,13 @@ def __init__(self, *args, **kwargs): if self.cfg['disable_werror']: general_opts.update(disable_werror) + if self.cfg['build_runtimes']: self.final_runtimes += ['compiler-rt', 'libunwind', 'libcxx', 'libcxxabi'] + if self.cfg['build_openmp']: self.final_projects.append('openmp') + if self.cfg['build_openmp_offload']: if not self.cfg['build_openmp']: raise EasyBuildError("Building OpenMP offload requires building OpenMP runtime") @@ -354,25 +311,31 @@ def __init__(self, *args, **kwargs): self.final_runtimes.append('offload') else: self.log.warning("OpenMP offloading is included with the OpenMP runtime for LLVM < 19") + if self.cfg['build_openmp_tools']: if not self.cfg['build_openmp']: raise EasyBuildError("Building OpenMP tools requires building OpenMP runtime") + if self.cfg['usepolly']: self.final_projects.append('polly') + if self.cfg['build_clang_extras']: self.final_projects.append('clang-tools-extra') + if self.cfg['build_lld']: self.intermediate_projects.append('lld') self.final_projects.append('lld') # This should be the default to make offload multi-stage compilations easier general_opts['CLANG_DEFAULT_LINKER'] = 'lld' general_opts['FLANG_DEFAULT_LINKER'] = 'lld' + if self.cfg['build_lldb']: self.final_projects.append('lldb') if self.full_llvm: remove_gcc_dependency_opts['LLDB_ENABLE_LIBXML2'] = 'Off' remove_gcc_dependency_opts['LLDB_ENABLE_LZMA'] = 'Off' remove_gcc_dependency_opts['LLDB_ENABLE_PYTHON'] = 'Off' + if self.cfg['build_bolt']: self.final_projects.append('bolt') @@ -402,17 +365,19 @@ def __init__(self, *args, **kwargs): # There are (old) toolchains with CUDA as part of the toolchain cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') if 'cuda' in deps or cuda_toolchain or cuda_cc_list: - build_targets += ['NVPTX'] + build_targets.append(BUILD_TARGET_NVPTX) self.offload_targets += ['cuda'] # Used for LLVM >= 19 - self.log.debug("NVPTX enabled by CUDA dependency/cuda_compute_capabilities") + self.log.debug(f"{BUILD_TARGET_NVPTX} enabled by CUDA dependency/cuda_compute_capabilities") + # For AMDGPU support we need ROCR-Runtime and # ROCT-Thunk-Interface, however, since ROCT is a dependency of # ROCR we only check for the ROCR-Runtime here # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler if 'rocr-runtime' in deps or amd_gfx_list: - build_targets += ['AMDGPU'] + build_targets.append(BUILD_TARGET_AMDGPU) self.offload_targets += ['amdgpu'] # Used for LLVM >= 19 - self.log.debug("AMDGPU enabled by rocr-runtime dependency/amd_gfx_list") + self.log.debug(f"{BUILD_TARGET_AMDGPU} enabled by rocr-runtime dependency/amd_gfx_list") + self.cfg['build_targets'] = build_targets self.log.debug("Using %s as default build targets for CPU architecture %s.", build_targets, arch) @@ -428,21 +393,19 @@ def __init__(self, *args, **kwargs): self.build_targets = build_targets or [] self.cuda_cc = [cc.replace('.', '') for cc in cuda_cc_list] - if 'NVPTX' in self.build_targets and not self.cuda_cc: + if BUILD_TARGET_NVPTX in self.build_targets and not self.cuda_cc: raise EasyBuildError("Can't build Clang with CUDA support without specifying 'cuda-compute-capabilities'") - if self.cuda_cc and 'NVPTX' not in self.build_targets: + if self.cuda_cc and BUILD_TARGET_NVPTX not in self.build_targets: print_warning("CUDA compute capabilities specified, but NVPTX not in manually specified build targets.") self.amd_gfx = amd_gfx_list - if 'AMDGPU' in self.build_targets and not self.amd_gfx: + if BUILD_TARGET_AMDGPU in self.build_targets and not self.amd_gfx: raise EasyBuildError("Can't build Clang with AMDGPU support without specifying 'amd_gfx_list'") - if self.amd_gfx and 'AMDGPU' not in self.build_targets: - print_warning("`amd_gfx` specified, but AMDGPU not in manually specified build targets.") + if self.amd_gfx and BUILD_TARGET_AMDGPU not in self.build_targets: + print_warning("'amd_gfx' specified, but AMDGPU not in manually specified build targets.") + general_opts['CMAKE_BUILD_TYPE'] = self.build_type general_opts['CMAKE_INSTALL_PREFIX'] = self.installdir - # if self.toolchain.options['pic']: - if self.cfg['use_pic']: - general_opts['CMAKE_POSITION_INDEPENDENT_CODE'] = 'ON' general_opts['LLVM_TARGETS_TO_BUILD'] = '"%s"' % ';'.join(build_targets) @@ -451,15 +414,15 @@ def __init__(self, *args, **kwargs): self.llvm_src_dir = os.path.join(self.builddir, 'llvm-project-%s.src' % self.version) def _add_cmake_runtime_args(self): - """Generate the value for `RUNTIMES_CMAKE_ARGS` and add it to the cmake options.""" + """Generate the value for 'RUNTIMES_CMAKE_ARGS' and add it to the cmake options.""" if self.runtimes_cmake_args: - tmp_list = [] + args = [] for key, val in self.runtimes_cmake_args.items(): if isinstance(val, list): val = ' '.join(val) if val: - tmp_list += ['-D%s=%s' % (key, val)] - self._cmakeopts['RUNTIMES_CMAKE_ARGS'] = '"%s"' % ';'.join(tmp_list) + args.append('-D%s=%s' % (key, val)) + self._cmakeopts['RUNTIMES_CMAKE_ARGS'] = '"%s"' % ';'.join(args) def _configure_general_build(self): """General configuration step for LLVM.""" @@ -490,11 +453,11 @@ def _configure_final_build(self): if not self.cfg['build_openmp_tools']: self._cmakeopts['OPENMP_ENABLE_OMPT_TOOLS'] = 'OFF' - # Make sure tests are not running with more than `--parallel` tasks - parallel = self.cfg['parallel'] + # Make sure tests are not running with more than 'parallel' tasks + parallel = self.cfg.parallel if not build_option('mpi_tests'): parallel = 1 - lit_args = ['-j %s' % parallel] + lit_args = [f'-j {parallel}'] if self.cfg['debug_tests']: lit_args += ['-v'] timeout_single = self.cfg['test_suite_timeout_single'] @@ -511,10 +474,32 @@ def _configure_final_build(self): self._cmakeopts['LLVM_INCLUDE_TESTS'] = 'ON' self._cmakeopts['LLVM_BUILD_TESTS'] = 'ON' + @staticmethod + def _get_gcc_prefix(): + """Get the GCC prefix for the build.""" + arch = get_cpu_architecture() + gcc_root = get_software_root('GCCcore') + gcc_version = get_software_version('GCCcore') + # If that doesn't work, try with GCC + if gcc_root is None: + gcc_root = get_software_root('GCC') + gcc_version = get_software_version('GCC') + # If that doesn't work either, print error and exit + if gcc_root is None: + raise EasyBuildError("Can't find GCC or GCCcore to use") + + pattern = os.path.join(gcc_root, 'lib', 'gcc', f'{arch}-*', gcc_version) + matches = glob.glob(pattern) + if not matches: + raise EasyBuildError("Can't find GCC version %s for architecture %s in %s", gcc_version, arch, pattern) + gcc_prefix = os.path.abspath(matches[0]) + + return gcc_root, gcc_prefix + def _set_gcc_prefix(self): """Set the GCC prefix for the build.""" if self.gcc_prefix is None: - gcc_root, gcc_prefix = get_gcc_prefix() + gcc_root, gcc_prefix = self._get_gcc_prefix() # --gcc-toolchain and --gcc-install-dir for flang are not supported before LLVM 19 # https://github.com/llvm/llvm-project/pull/87360 @@ -523,7 +508,7 @@ def _set_gcc_prefix(self): general_opts['GCC_INSTALL_PREFIX'] = gcc_root else: # See https://github.com/llvm/llvm-project/pull/85891#issuecomment-2021370667 - self.log.debug("Using `--gcc-install-dir` in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") + self.log.debug("Using '--gcc-install-dir' in CMAKE_C_FLAGS and CMAKE_CXX_FLAGS") self.runtimes_cmake_args['CMAKE_C_FLAGS'] += ['--gcc-install-dir=%s' % gcc_prefix] self.runtimes_cmake_args['CMAKE_CXX_FLAGS'] += ['--gcc-install-dir=%s' % gcc_prefix] @@ -547,18 +532,18 @@ def configure_step(self): lit_root = get_software_root('lit') if not lit_root: if not self.cfg['skip_all_tests']: - raise EasyBuildError("Can't find `lit`, needed for running tests-suite") + raise EasyBuildError("Can't find 'lit', needed for running tests-suite") timeouts = self.cfg['test_suite_timeout_single'] or self.cfg['test_suite_timeout_total'] if not self.cfg['skip_all_tests'] and timeouts: psutil_root = get_software_root('psutil') if not psutil_root: - raise EasyBuildError("Can't find `psutil`, needed for running tests-suite with timeout") + raise EasyBuildError("Can't find 'psutil', needed for running tests-suite with timeout") # Parallel build self.make_parallel_opts = "" - if self.cfg['parallel']: - self.make_parallel_opts = "-j %s" % self.cfg['parallel'] + if self.cfg.parallel: + self.make_parallel_opts = f"-j {self.cfg.parallel}" # Bootstrap self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') @@ -580,13 +565,12 @@ def configure_step(self): xml2_root = get_software_root('libxml2') if xml2_root: if self.full_llvm: - self.log.warning("LLVM is being built in `full_llvm` mode, libxml2 will not be used") + self.log.warning("LLVM is being built in 'full_llvm' mode, libxml2 will not be used") else: general_opts['LLVM_ENABLE_LIBXML2'] = 'ON' - # general_opts['LIBXML2_ROOT'] = xml2_root - # If `ON`, risk finding a system zlib or zstd leading to including /usr/include as -isystem that can lead - # to errors during compilation of `offload.tools.kernelreplay` due to the inclusion of LLVMSupport (19.x) + # If 'ON', risk finding a system zlib or zstd leading to including /usr/include as -isystem that can lead + # to errors during compilation of 'offload.tools.kernelreplay' due to the inclusion of LLVMSupport (19.x) general_opts['LLVM_ENABLE_ZLIB'] = 'ON' if get_software_root('zlib') else 'OFF' general_opts['LLVM_ENABLE_ZSTD'] = 'ON' if get_software_root('zstd') else 'OFF' # Should not use system SWIG if present @@ -604,12 +588,8 @@ def configure_step(self): general_opts.update(python_opts) self.runtimes_cmake_args.update(python_opts) - self.llvm_obj_dir_stage1 = os.path.join(self.builddir, 'llvm.obj.1') if self.cfg['bootstrap']: self._configure_intermediate_build() - # if self.full_llvm: - # self.intermediate_projects.append('libc') - # self.final_projects.append('libc') else: self._configure_final_build() @@ -641,17 +621,13 @@ def configure_step(self): self._configure_general_build() self.add_cmake_opts() - super(EB_LLVM, self).configure_step( - builddir=self.llvm_obj_dir_stage1, - srcdir=os.path.join(self.llvm_src_dir, "llvm") - ) + src_dir = os.path.join(self.llvm_src_dir, 'llvm') + super(EB_LLVM, self).configure_step(builddir=self.llvm_obj_dir_stage1, srcdir=src_dir) def disable_sanitizer_tests(self): """Disable the tests of all the sanitizers by removing the test directories from the build system""" cmakelists_tests = os.path.join(self.llvm_src_dir, 'compiler-rt', 'test', 'CMakeLists.txt') - regex_subs = [] - regex_subs.append((r'compiler_rt_test_runtime.*san.*', '')) - + regex_subs = [(r'compiler_rt_test_runtime.*san.*', '')] apply_regex_substitutions(cmakelists_tests, regex_subs) def add_cmake_opts(self): @@ -677,6 +653,14 @@ def configure_step3(self): if self.full_llvm: self._cmakeopts.update(remove_gcc_dependency_opts) + @staticmethod + def _create_compiler_config_file(compilers, gcc_prefix, installdir): + """Create a config file for the compiler to point to the correct GCC installation.""" + bin_dir = os.path.join(installdir, 'bin') + prefix_str = '--gcc-install-dir=%s' % gcc_prefix + for comp in compilers: + write_file(os.path.join(bin_dir, f'{comp}.cfg'), prefix_str) + def build_with_prev_stage(self, prev_dir, stage_dir): """Build LLVM using the previous stage.""" curdir = os.getcwd() @@ -697,7 +681,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # rpath-wrapped. This causes the executable to be produced without rpath (if required) and with # runpath set to $ORIGIN. This causes 2 problems: # - Binaries produced for the runtimes will fail the sanity check - # - Runtimes libraries that link to libLLVM.so like `libomptarget.so` need LD_LIBRARY_PATH to work. + # - Runtimes libraries that link to libLLVM.so like 'libomptarget.so' need LD_LIBRARY_PATH to work. # This is because even if an executable compiled with the new llvm has rpath pointing to $EBROOTLLVM/lib, # it will not be resolved with the executable's rpath, but the library's runpath # (rpath is ignored if runpath is set). @@ -718,21 +702,15 @@ def build_with_prev_stage(self, prev_dir, stage_dir): copy_file(prev_clangxx, nxt_clangxx) tmp_toolchain = Clang(name='Clang', version='1') - tmp_toolchain.prepare_rpath_wrappers( - rpath_include_dirs=[ - # Don't need stage dir here as LD_LIBRARY_PATH is set during build, this is only needed for - # installed binaries with rpath - os.path.join(self.installdir, 'lib'), - os.path.join(self.installdir, 'lib64'), - os.path.join(self.installdir, lib_dir_runtime), - ] - ) + # Don't need stage dir here as LD_LIBRARY_PATH is set during build, this is only needed for + # installed binaries with rpath + lib_dirs = [os.path.join(self.installdir, x) for x in SEARCH_PATH_LIB_DIRS + [lib_dir_runtime]] + tmp_toolchain.prepare_rpath_wrappers(rpath_include_dirs=lib_dirs) remove_file(nxt_clang) remove_file(nxt_clangxx) - self.log.info( - "Prepared MOCK rpath wrappers needed to rpath-wrap also the new compilers produced " - "by the project build and than used for the runtimes build" - ) + msg = "Prepared MOCK rpath wrappers needed to rpath-wrap also the new compilers produced " + msg += "by the project build and than used for the runtimes build" + self.log.info(msg) clang_mock = which('clang') clangxx_mock = which('clang++') @@ -799,22 +777,22 @@ def build_with_prev_stage(self, prev_dir, stage_dir): # Also runs of the intermediate step compilers should be made aware of the GCC installation if LooseVersion(self.version) >= LooseVersion('19'): self._set_gcc_prefix() - create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, prev_dir) + self._create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, prev_dir) self.add_cmake_opts() change_dir(stage_dir) self.log.debug("Configuring %s", stage_dir) - cmd = "cmake %s %s" % (self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')) + cmd = ' '.join(['cmake', self.cfg['configopts'], os.path.join(self.llvm_src_dir, 'llvm')]) run_shell_cmd(cmd) self.log.debug("Building %s", stage_dir) - cmd = "make %s VERBOSE=1" % self.make_parallel_opts + cmd = f"make {self.make_parallel_opts} VERBOSE=1" run_shell_cmd(cmd) change_dir(curdir) - def build_step(self, verbose=False, path=None): + def build_step(self, *args, **kwargs): """Build LLVM, and optionally build it using itself.""" if self.cfg['bootstrap']: self.log.info("Building stage 1") @@ -822,8 +800,10 @@ def build_step(self, verbose=False, path=None): else: self.log.info("Building LLVM") print_msg("Building stage 1/1") + change_dir(self.llvm_obj_dir_stage1) - super(EB_LLVM, self).build_step(verbose, path) + super(EB_LLVM, self).build_step(*args, **kwargs) + if self.cfg['bootstrap']: self.log.info("Building stage 2") print_msg("Building stage 2/3") @@ -845,7 +825,7 @@ def _para_test_step(self, parallel=1): lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) lib_path = os.path.join(basedir, lib_dir_runtime) with _wrap_env(os.path.join(basedir, 'bin'), lib_path): - cmd = "make -j %s check-all" % parallel + cmd = f"make -j {parallel} check-all" res = run_shell_cmd(cmd, fail_on_error=False) out = res.output self.log.debug(out) @@ -880,21 +860,18 @@ def test_step(self): # Also runs of test suite compilers should be made aware of the GCC installation if LooseVersion(self.version) >= LooseVersion('19'): self._set_gcc_prefix() - create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.final_dir) + self._create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.final_dir) max_failed = self.cfg['test_suite_max_failed'] - # self.log.info("Running test-suite with parallel jobs") - # num_failed = self._para_test_step(parallel=self.cfg['parallel']) - # if num_failed is None: - # self.log.warning("Tests with parallel jobs failed, retrying with single job") - # num_failed = self._para_test_step(parallel=1) num_failed = self._para_test_step(parallel=1) if num_failed is None: raise EasyBuildError("Failed to extract test results from output") if num_failed > max_failed: - raise EasyBuildError("Too many failed tests: %s (%s allowed)", num_failed, max_failed) - - self.log.info("Test-suite completed with %s failed tests (%s allowed)", num_failed, max_failed) + raise EasyBuildError(f"Too many failed tests: {num_failed} ({max_failed} allowed)") + elif num_failed: + self.log.info(f"Test suite completed with {num_failed} failed tests ({max_failed} allowed)") + else: + self.log.info(f"Test suite completed, no failed tests ({max_failed} allowed)") def install_step(self): """Install stage 1 or 3 (if bootstrap) binaries.""" @@ -910,10 +887,7 @@ def install_step(self): orig_ld_library_path ]) - # _preinstallopts = self.cfg.get('preinstallopts', '') - self.cfg.update('preinstallopts', ' '.join([ - 'LD_LIBRARY_PATH=%s' % lib_path - ])) + self.cfg.update('preinstallopts', f'LD_LIBRARY_PATH={lib_path}') super(EB_LLVM, self).install_step() @@ -923,18 +897,18 @@ def post_install_step(self): # copy Python bindings here in post-install step so that it is not done more than once in multi_deps context if self.cfg['python_bindings']: - python_bindings_source_dir = os.path.join(self.llvm_src_dir, "clang", "bindings", "python") + python_bindings_source_dir = os.path.join(self.llvm_src_dir, 'clang', 'bindings', 'python') python_bindins_target_dir = os.path.join(self.installdir, 'lib', 'python') copy_dir(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) - python_bindings_source_dir = os.path.join(self.llvm_src_dir, "mlir", "python") + python_bindings_source_dir = os.path.join(self.llvm_src_dir, 'mlir', 'python') copy_dir(python_bindings_source_dir, python_bindins_target_dir, dirs_exist_ok=True) if LooseVersion(self.version) >= LooseVersion('19'): # For GCC aware installation create config files in order to point to the correct GCC installation # Required as GCC_INSTALL_PREFIX was removed (see https://github.com/llvm/llvm-project/pull/87360) self._set_gcc_prefix() - create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.installdir) + self._create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.installdir) # This is needed as some older build system will select a different naming scheme for the library leading to # The correct target <__config_site> and libclang_rt.builtins.a not being found @@ -947,9 +921,8 @@ def post_install_step(self): src = os.path.join(self.installdir, dirname, orig) dst = os.path.join(self.installdir, dirname, other) if os.path.exists(src) and not os.path.exists(dst): - self.log.info( - "Creating symlink for %s to %s for better compatibility with expected --target", src, dst - ) + msg = f"Creating symlink for {src} to {dst} for better compatibility with expected --target" + self.log.info(msg) symlink(src, dst) def get_runtime_lib_path(self, base_dir, fail_ok=True): @@ -964,7 +937,7 @@ def get_runtime_lib_path(self, base_dir, fail_ok=True): if not fail_ok: raise EasyBuildError("Could not find runtime library directory") print_warning("Could not find runtime library directory") - res = "lib" + res = 'lib' return res @@ -980,6 +953,22 @@ def banned_linked_shared_libs(self): res += ['libc++', 'libc++abi', 'libunwind'] return res + @staticmethod + def _sanity_check_gcc_prefix(compilers, gcc_prefix, installdir): + """Check if the GCC prefix of the compiler is correct""" + rgx = re.compile('Selected GCC installation: (.*)') + for comp in compilers: + cmd = "%s -v" % os.path.join(installdir, 'bin', comp) + res = run_shell_cmd(cmd, fail_on_error=False) + out = res.output + mch = rgx.search(out) + if mch is None: + raise EasyBuildError("Failed to extract GCC installation path from output of '%s': %s", cmd, out) + check_prefix = mch.group(1) + if check_prefix != gcc_prefix: + error_msg = "GCC installation path '{check_prefix}' does not match expected path '{gcc_prefix}'" + raise EasyBuildError(error_msg) + def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None): """Perform sanity checks on the installed LLVM.""" lib_dir_runtime = None @@ -1006,7 +995,10 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_inc_files = [] check_dirs = ['include/llvm', 'include/llvm-c', 'lib/cmake/llvm'] custom_commands = [ - 'llvm-ar --help', 'llvm-ranlib --help', 'llvm-nm --help', 'llvm-objdump --help', + "llvm-ar --help", + "llvm-ranlib --help", + "llvm-nm --help", + "llvm-objdump --help", ] gcc_prefix_compilers = [] if self.build_shared: @@ -1071,14 +1063,6 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F 'libmlir_float16_utils.so' ] check_dirs += ['lib/cmake/mlir', 'include/mlir', 'include/mlir-c'] - # if 'compiler-rt' in self.final_runtimes: - # pth = os.path.join('lib', 'clang', resdir_version, lib_dir_runtime) - # check_files += [os.path.join(pth, _) for _ in [ - # # This should probably be more finetuned depending on what features of compiler-rt are used - # 'libclang_rt.xray.a', 'libclang_rt.fuzzer.a', 'libclang_rt.gwp_asan.a', 'libclang_rt.profile.a', - # 'libclang_rt.lsan.a', 'libclang_rt.asan.a', 'libclang_rt.hwasan.a' - # ]] - # check_dirs += ['include/sanitizer', 'include/fuzzer', 'include/orc', 'include/xray'] if 'libunwind' in self.final_runtimes: check_librt_files += ['libunwind.a'] if self.build_shared: @@ -1128,7 +1112,7 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F omp_lib_files += ['llibomptarget-amdgpu-%s.bc' % gfx for gfx in self.amd_gfx] if LooseVersion(self.version) < LooseVersion('19'): - # Before LLVM 19, omp related libraries are installed under `ROOT/lib`` + # Before LLVM 19, omp related libraries are installed under 'ROOT/lib'' check_lib_files += omp_lib_files else: # Starting from LLVM 19, omp related libraries are installed the runtime library directory @@ -1148,10 +1132,10 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F check_lib_files.append(libext) check_lib_files.remove(libso) - check_files += [os.path.join('bin', _) for _ in check_bin_files] - check_files += [os.path.join('lib', _) for _ in check_lib_files] - check_files += [os.path.join(lib_dir_runtime, _) for _ in check_librt_files] - check_files += [os.path.join('include', _) for _ in check_inc_files] + check_files += [os.path.join('bin', x) for x in check_bin_files] + check_files += [os.path.join('lib', x) for x in check_lib_files] + check_files += [os.path.join(lib_dir_runtime, x) for x in check_librt_files] + check_files += [os.path.join('include', x) for x in check_inc_files] custom_paths = { 'files': check_files, @@ -1160,14 +1144,34 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F self._set_gcc_prefix() if lib_dir_runtime: - # Required for `clang -v` to work if linked to LLVM runtimes + # Required for 'clang -v' to work if linked to LLVM runtimes with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)): - sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) + self._sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) else: - sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) + self._sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir) return super(EB_LLVM, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands) + def make_module_step(self, *args, **kwargs): + """ + Clang can find its own headers and libraries but the shared libraries need to be in $LD_LIBRARY_PATH + """ + mod_env_headers = self.module_load_environment.alias_vars(MODULE_LOAD_ENV_HEADERS) + for disallowed_var in mod_env_headers: + self.module_load_environment.remove(disallowed_var) + self.log.debug(f"Purposely not updating ${disallowed_var} in {self.name} module file") + + lib_dirs = SEARCH_PATH_LIB_DIRS[:] + if self.cfg['build_runtimes']: + runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) + lib_dirs.append(runtime_lib_path) + + self.log.debug(f"List of subdirectories for libraries to add to $LD_LIBRARY_PATH + $LIBRARY_PATH: {lib_dirs}") + self.module_load_environment.LD_LIBRARY_PATH = lib_dirs + self.module_load_environment.LIBRARY_PATH = lib_dirs + + return super().make_module_step(*args, **kwargs) + def make_module_extra(self): """Custom variables for Clang module.""" txt = super(EB_LLVM, self).make_module_extra() @@ -1175,21 +1179,5 @@ def make_module_extra(self): asan_symbolizer_path = os.path.join(self.installdir, 'bin', 'llvm-symbolizer') txt += self.module_generator.set_environment('ASAN_SYMBOLIZER_PATH', asan_symbolizer_path) if self.cfg['python_bindings']: - txt += self.module_generator.prepend_paths('PYTHONPATH', os.path.join("lib", "python")) + txt += self.module_generator.prepend_paths('PYTHONPATH', os.path.join('lib', 'python')) return txt - - def make_module_req_guess(self): - """ - Clang can find its own headers and libraries but the .so's need to be in LD_LIBRARY_PATH - """ - libs = ['lib', 'lib64'] - if self.cfg['build_runtimes']: - runtime_lib_path = self.get_runtime_lib_path(self.installdir, fail_ok=False) - libs.append(runtime_lib_path) - guesses = super(EB_LLVM, self).make_module_req_guess() - guesses.update({ - 'CPATH': [], - 'LIBRARY_PATH': libs, - 'LD_LIBRARY_PATH': libs, - }) - return guesses From 939173ddd2dba04564ef56fc374122f3bce9910f Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 5 Mar 2025 17:22:22 +0100 Subject: [PATCH 095/103] Added check to not auto-enable GPU targets if LLVM<18 --- easybuild/easyblocks/l/llvm.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 7bff2537328..112ad3bc157 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -365,18 +365,24 @@ def __init__(self, *args, **kwargs): # There are (old) toolchains with CUDA as part of the toolchain cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') if 'cuda' in deps or cuda_toolchain or cuda_cc_list: - build_targets.append(BUILD_TARGET_NVPTX) - self.offload_targets += ['cuda'] # Used for LLVM >= 19 - self.log.debug(f"{BUILD_TARGET_NVPTX} enabled by CUDA dependency/cuda_compute_capabilities") + if LooseVersion(self.version) < LooseVersion('18'): + self.log.warning("CUDA support is only available in LLVM >= 18") + else: + build_targets.append(BUILD_TARGET_NVPTX) + self.offload_targets += ['cuda'] # Used for LLVM >= 19 + self.log.debug(f"{BUILD_TARGET_NVPTX} enabled by CUDA dependency/cuda_compute_capabilities") # For AMDGPU support we need ROCR-Runtime and # ROCT-Thunk-Interface, however, since ROCT is a dependency of # ROCR we only check for the ROCR-Runtime here # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler if 'rocr-runtime' in deps or amd_gfx_list: - build_targets.append(BUILD_TARGET_AMDGPU) - self.offload_targets += ['amdgpu'] # Used for LLVM >= 19 - self.log.debug(f"{BUILD_TARGET_AMDGPU} enabled by rocr-runtime dependency/amd_gfx_list") + if LooseVersion(self.version) < LooseVersion('18'): + self.log.warning("AMDGPU support is only available in LLVM >= 18") + else: + build_targets.append(BUILD_TARGET_AMDGPU) + self.offload_targets += ['amdgpu'] # Used for LLVM >= 19 + self.log.debug(f"{BUILD_TARGET_AMDGPU} enabled by rocr-runtime dependency/amd_gfx_list") self.cfg['build_targets'] = build_targets self.log.debug("Using %s as default build targets for CPU architecture %s.", build_targets, arch) From 4f0eeee8c3e06b2887aa3bca10cb94344d0fc3ee Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Wed, 5 Mar 2025 17:34:23 +0100 Subject: [PATCH 096/103] tweak log message for not auto-enabling offload targets for LLVM < 18 --- easybuild/easyblocks/l/llvm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 112ad3bc157..2e151b55f67 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -366,7 +366,7 @@ def __init__(self, *args, **kwargs): cuda_toolchain = hasattr(self.toolchain, 'COMPILER_CUDA_FAMILY') if 'cuda' in deps or cuda_toolchain or cuda_cc_list: if LooseVersion(self.version) < LooseVersion('18'): - self.log.warning("CUDA support is only available in LLVM >= 18") + self.log.info(f"Not auto-enabling {BUILD_TARGET_NVPTX} offload target, only done for LLVM >= 18") else: build_targets.append(BUILD_TARGET_NVPTX) self.offload_targets += ['cuda'] # Used for LLVM >= 19 @@ -378,7 +378,7 @@ def __init__(self, *args, **kwargs): # https://openmp.llvm.org/SupportAndFAQ.html#q-how-to-build-an-openmp-amdgpu-offload-capable-compiler if 'rocr-runtime' in deps or amd_gfx_list: if LooseVersion(self.version) < LooseVersion('18'): - self.log.warning("AMDGPU support is only available in LLVM >= 18") + self.log.info(f"Not auto-enabling {BUILD_TARGET_AMDGPU} offload target, only done for LLVM >= 18") else: build_targets.append(BUILD_TARGET_AMDGPU) self.offload_targets += ['amdgpu'] # Used for LLVM >= 19 From a7abb7be3d109a28f77e2c8561634e3cfb60554b Mon Sep 17 00:00:00 2001 From: crivella Date: Wed, 5 Mar 2025 18:17:39 +0100 Subject: [PATCH 097/103] Ensure compatibility with rpath wrappers and `-Werror` in tests --- easybuild/easyblocks/l/llvm.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 2e151b55f67..fbf63148af8 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -830,11 +830,18 @@ def _para_test_step(self, parallel=1): if self.cfg['build_runtimes']: lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) lib_path = os.path.join(basedir, lib_dir_runtime) + old_cflags = os.getenv('CFLAGS', '') + old_cxxflags = os.getenv('CXXFLAGS', '') + if build_option('rpath'): + setvar('CFLAGS', "%s %s" % (old_cflags, '-Wno-unused-command-line-argument')) + setvar('CXXFLAGS', "%s %s" % (old_cxxflags, '-Wno-unused-command-line-argument')) with _wrap_env(os.path.join(basedir, 'bin'), lib_path): cmd = f"make -j {parallel} check-all" res = run_shell_cmd(cmd, fail_on_error=False) out = res.output self.log.debug(out) + setvar('CFLAGS', old_cflags) + setvar('CXXFLAGS', old_cxxflags) rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) From d726a99c52a9891ff89af95269f74f2244e150b6 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 6 Mar 2025 10:09:46 +0100 Subject: [PATCH 098/103] Fixed check location for `libarcher.so` changed in LLVM>=19 --- easybuild/easyblocks/l/llvm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index fbf63148af8..43643ec9fb8 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -1134,7 +1134,10 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] - check_librt_files += ['libarcher.so'] + if LooseVersion(self.version) < LooseVersion('19'): + check_lib_files += ['libomp.so'] + elif LooseVersion(self.version) >= LooseVersion('19'): + check_librt_files += ['libomp.so'] if self.cfg['python_bindings']: custom_commands += ["python -c 'import clang'"] custom_commands += ["python -c 'import mlir'"] From 095f6c7d1b8fc18fd0f9f16cfbbb2a317100d743 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 6 Mar 2025 10:42:24 +0100 Subject: [PATCH 099/103] Fix wrong name --- easybuild/easyblocks/l/llvm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 43643ec9fb8..8f4d41bce4c 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -1135,9 +1135,9 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F if self.cfg['build_openmp_tools']: check_files += [os.path.join('lib', 'clang', resdir_version, 'include', 'ompt.h')] if LooseVersion(self.version) < LooseVersion('19'): - check_lib_files += ['libomp.so'] + check_lib_files += ['libarcher.so'] elif LooseVersion(self.version) >= LooseVersion('19'): - check_librt_files += ['libomp.so'] + check_librt_files += ['libarcher.so'] if self.cfg['python_bindings']: custom_commands += ["python -c 'import clang'"] custom_commands += ["python -c 'import mlir'"] From 489b026e882a4bb96b09414adfea22cf64257118 Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 6 Mar 2025 10:55:25 +0100 Subject: [PATCH 100/103] Added comment on rpath + test fix for LLVM<19 --- easybuild/easyblocks/l/llvm.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 8f4d41bce4c..b99894aff22 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -830,8 +830,15 @@ def _para_test_step(self, parallel=1): if self.cfg['build_runtimes']: lib_dir_runtime = self.get_runtime_lib_path(basedir, fail_ok=False) lib_path = os.path.join(basedir, lib_dir_runtime) + + # When rpath is enabled, the easybuild rpath wrapper will be used for compiling the tests + # A combination of -Werror and the wrapper translating LD_LIBRARY_PATH to -Wl,... flags will results in failing + # tests due to -Wunused-command-line-argument + # This has shown to be a problem in builds for 18.1.8, but seems it was not necessary for LLVM >= 19 + # needs more digging into the CMake logic old_cflags = os.getenv('CFLAGS', '') old_cxxflags = os.getenv('CXXFLAGS', '') + # TODO: Find a better way to either force the test to use the non wrapped compiler or to pass the flags if build_option('rpath'): setvar('CFLAGS', "%s %s" % (old_cflags, '-Wno-unused-command-line-argument')) setvar('CXXFLAGS', "%s %s" % (old_cxxflags, '-Wno-unused-command-line-argument')) @@ -840,6 +847,8 @@ def _para_test_step(self, parallel=1): res = run_shell_cmd(cmd, fail_on_error=False) out = res.output self.log.debug(out) + + # Reset the CFLAGS and CXXFLAGS setvar('CFLAGS', old_cflags) setvar('CXXFLAGS', old_cxxflags) From a428713836c46077b6411cb2e460787ae5e74eec Mon Sep 17 00:00:00 2001 From: crivella Date: Thu, 6 Mar 2025 13:26:19 +0100 Subject: [PATCH 101/103] WIP - ignorable test patterns --- easybuild/easyblocks/l/llvm.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index b99894aff22..7cdecaf3377 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -212,6 +212,7 @@ def extra_options(): 'python_bindings': [False, "Install python bindings", CUSTOM], 'skip_all_tests': [False, "Skip running of tests", CUSTOM], 'skip_sanitizer_tests': [True, "Do not run the sanitizer tests", CUSTOM], + 'test_suite_ignore_patterns': [None, "List of test to ignore (if the string matches)", CUSTOM], 'test_suite_max_failed': [0, "Maximum number of failing tests (does not count allowed failures)", CUSTOM], 'test_suite_timeout_single': [None, "Timeout for each individual test in the test suite", CUSTOM], 'test_suite_timeout_total': [None, "Timeout for total running time of the testsuite", CUSTOM], @@ -825,6 +826,17 @@ def _para_test_step(self, parallel=1): """Run test suite with the specified number of parallel jobs for make.""" basedir = self.final_dir + # From grep -E "^[A-Z]+: " LOG_FILE | cut -d: -f1 | sort | uniq + OUTCOMES_LOG = [ + 'FAIL', + 'TIMEOUT', + ] + # OUTCOMES_OK = [ + # 'PASS', + # 'UNSUPPORTED', + # 'XFAIL', + # ] + change_dir(basedir) lib_path = '' if self.cfg['build_runtimes']: @@ -852,6 +864,16 @@ def _para_test_step(self, parallel=1): setvar('CFLAGS', old_cflags) setvar('CXXFLAGS', old_cxxflags) + ignore_patterns = self.cfg['test_suite_ignore_patterns'] or [] + ignored_pattern = 0 + failed_pattern = 0 + for line in out.splitlines(): + if any(line.startswith(f'{x}: ') for x in OUTCOMES_LOG): + if any(patt in line for patt in ignore_patterns): + self.log.info("Ignoring test failure: %s", line) + ignored_pattern += 1 + failed_pattern += 1 + rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) if mch is None: @@ -874,6 +896,15 @@ def _para_test_step(self, parallel=1): self.log.info("Tests timed out: %s", num_timed_out) num_failed += num_timed_out + if num_failed != failed_pattern: + msg = f"Number of failed tests {num_failed} does not match " + msg += f"number identified from line by line patterns {failed_pattern}" + self.log.warning(msg) + + if ignored_pattern: + self.log.info("Ignored %s failed tests due to ignore patterns", ignored_pattern) + num_failed -= ignored_pattern + return num_failed def test_step(self): From 6b5cfdfe7161461853973431c144911d7d66d31a Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Thu, 6 Mar 2025 16:37:57 +0100 Subject: [PATCH 102/103] minor code cleanup for support for ignoring specific tests in LLVM easyblock --- easybuild/easyblocks/l/llvm.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index 7cdecaf3377..da747b41ac8 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -865,14 +865,15 @@ def _para_test_step(self, parallel=1): setvar('CXXFLAGS', old_cxxflags) ignore_patterns = self.cfg['test_suite_ignore_patterns'] or [] - ignored_pattern = 0 - failed_pattern = 0 - for line in out.splitlines(): - if any(line.startswith(f'{x}: ') for x in OUTCOMES_LOG): - if any(patt in line for patt in ignore_patterns): - self.log.info("Ignoring test failure: %s", line) - ignored_pattern += 1 - failed_pattern += 1 + ignored_pattern_matches = 0 + failed_pattern_matches = 0 + if ignore_patterns: + for line in out.splitlines(): + if any(line.startswith(f'{x}: ') for x in OUTCOMES_LOG): + if any(patt in line for patt in ignore_patterns): + self.log.info("Ignoring test failure: %s", line) + ignored_pattern_matches += 1 + failed_pattern_matches += 1 rgx_failed = re.compile(r'^ +Failed +: +([0-9]+)', flags=re.MULTILINE) mch = rgx_failed.search(out) @@ -896,14 +897,14 @@ def _para_test_step(self, parallel=1): self.log.info("Tests timed out: %s", num_timed_out) num_failed += num_timed_out - if num_failed != failed_pattern: - msg = f"Number of failed tests {num_failed} does not match " - msg += f"number identified from line by line patterns {failed_pattern}" + if num_failed != failed_pattern_matches: + msg = f"Number of failed tests ({num_failed}) does not match " + msg += f"number identified va line-by-line pattern matching: {failed_pattern_matches}" self.log.warning(msg) - if ignored_pattern: - self.log.info("Ignored %s failed tests due to ignore patterns", ignored_pattern) - num_failed -= ignored_pattern + if ignored_pattern_matches: + self.log.info("Ignored %s failed tests due to ignore patterns", ignored_pattern_matches) + num_failed -= ignored_pattern_matches return num_failed From 77c65ca72ab09ee91f8d333c473f16f79c613b05 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Thu, 6 Mar 2025 16:38:40 +0100 Subject: [PATCH 103/103] stop using deprecated 'post_install_step' name in LLVM easyblock, use 'post_processing_step' instead --- easybuild/easyblocks/l/llvm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py index da747b41ac8..f6c18727859 100644 --- a/easybuild/easyblocks/l/llvm.py +++ b/easybuild/easyblocks/l/llvm.py @@ -945,9 +945,9 @@ def install_step(self): super(EB_LLVM, self).install_step() - def post_install_step(self): + def post_processing_step(self): """Install python bindings.""" - super(EB_LLVM, self).post_install_step() + super(EB_LLVM, self).post_processing_step() # copy Python bindings here in post-install step so that it is not done more than once in multi_deps context if self.cfg['python_bindings']: