Skip to content
Snippets Groups Projects
aocc.py 7.33 KiB
Newer Older
  • Learn to ignore specific revisions
  • ##
    # Copyright 2020-2021 Forschungszentrum Juelich GmbH
    #
    # This file is triple-licensed under GPLv2 (see below), MIT, and
    # BSD three-clause licenses.
    #
    # 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 <http://www.gnu.org/licenses/>.
    ##
    """
    Support for installing AOCC, implemented as an easyblock.
    
    @author: Sebastian Achilles (Forschungszentrum Juelich GmbH)
    """
    
    import os
    import stat
    
    from distutils.version import LooseVersion
    
    from easybuild.easyblocks.generic.packedbinary import PackedBinary
    from easybuild.framework.easyconfig import CUSTOM
    from easybuild.tools.build_log import EasyBuildError
    from easybuild.tools.filetools import adjust_permissions, move_file, write_file
    from easybuild.tools.systemtools import get_shared_lib_ext
    
    # Wrapper script definition
    WRAPPER_TEMPLATE = """#!/bin/sh
    
    %(compiler_name)s --gcc-toolchain=$EBROOTGCCCORE "$@"
    """
    
    
    class EB_AOCC(PackedBinary):
        """
        Support for installing the AOCC compilers
        """
    
        @staticmethod
        def extra_options():
            extra_vars = {
                'clangversion': [None, "Clang Version on which AOCC is based on (10.0.0 or 11.0.0 or ...)", CUSTOM],
            }
            return PackedBinary.extra_options(extra_vars)
    
        def __init__(self, *args, **kwargs):
            """Easyblock constructor, define custom class variables specific to AOCC."""
            super(EB_AOCC, self).__init__(*args, **kwargs)
    
            self.clangversion = self.cfg['clangversion']
    
        def _aocc_guess_clang_version(self):
            map_aocc_to_clang_ver = {
                '2.3.0': '11.0.0',
                '3.0.0': '12.0.0',
                '3.1.0': '12.0.0',
            }
    
            if self.version in map_aocc_to_clang_ver:
                return map_aocc_to_clang_ver[self.version]
            else:
                error_lines = [
                    "AOCC is based on Clang. Guessing Clang version in easyblock failed.",
                    "You should either:",
                    "- specify `clangversion` in the easyconfig;",
                    "- extend `map_aocc_to_clang_ver` in the easyblock;",
                ]
                raise EasyBuildError('\n'.join(error_lines))
    
        def install_step(self):
            # EULA for AOCC must be accepted via --accept-eula-for EasyBuild configuration option,
            # or via 'accept_eula = True' in easyconfig file
            self.check_accepted_eula(more_info='http://developer.amd.com/wordpress/media/files/AOCC_EULA.pdf')
    
            # AOCC is based on Clang. Try to guess the clangversion from the AOCC version
            # if clangversion is not specified in the easyconfig
            if self.clangversion is None:
                self.clangversion = self._aocc_guess_clang_version()
    
            super(EB_AOCC, self).install_step()
    
        def post_install_step(self):
            """Create wrappers for the compilers to make sure compilers picks up GCCcore as GCC toolchain"""
    
            orig_compiler_tmpl = '%s.orig'
    
            def create_wrapper(wrapper_comp):
                """Create for a particular compiler, with a particular name"""
                wrapper_f = os.path.join(self.installdir, 'bin', wrapper_comp)
                write_file(wrapper_f, WRAPPER_TEMPLATE % {'compiler_name': orig_compiler_tmpl % wrapper_comp})
                perms = stat.S_IXUSR | stat.S_IRUSR | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH
                adjust_permissions(wrapper_f, perms)
    
            compilers_to_wrap = [
                'clang',
                'clang++',
                'clang-%s' % LooseVersion(self.clangversion).version[0],
                'clang-cpp',
                'flang',
            ]
    
            # Rename original compilers and prepare wrappers to pick up GCCcore as GCC toolchain for the compilers
            for comp in compilers_to_wrap:
                actual_compiler = os.path.join(self.installdir, 'bin', comp)
                if os.path.isfile(actual_compiler):
                    move_file(actual_compiler, orig_compiler_tmpl % actual_compiler)
                else:
                    err_str = "Tried to move '%s' to '%s', but it does not exist!"
                    raise EasyBuildError(err_str, actual_compiler, '%s.orig' % actual_compiler)
    
                if not os.path.exists(actual_compiler):
                    create_wrapper(comp)
                    self.log.info("Wrapper for %s successfully created", comp)
                else:
                    err_str = "Creating wrapper for '%s' not possible, since original compiler was not renamed!"
                    raise EasyBuildError(err_str, actual_compiler)
    
            super(EB_AOCC, self).post_install_step()
    
        def sanity_check_step(self):
            """Custom sanity check for AOCC, based on sanity check for Clang."""
            shlib_ext = get_shared_lib_ext()
            custom_paths = {
                'files': [
                    'bin/clang', 'bin/clang++', 'bin/flang', 'bin/lld', 'bin/llvm-ar', 'bin/llvm-as', 'bin/llvm-config',
                    'bin/llvm-link', 'bin/llvm-nm', 'bin/llvm-symbolizer', 'bin/opt', 'bin/scan-build', 'bin/scan-view',
                    'include/clang-c/Index.h', 'include/llvm-c/Core.h', 'lib/clang/%s/include/omp.h' % self.clangversion,
                    'lib/clang/%s/include/stddef.h' % self.clangversion, 
                    'lib/libclang.%s' % shlib_ext, 'lib/libomp.%s' % shlib_ext,
                ],
                'dirs': ['include/llvm', 'lib/clang/%s/lib' % self.clangversion, 'lib32'],
            }
    
            custom_commands = [
                "clang --help",
                "clang++ --help",
                "clang-%s --help" % LooseVersion(self.clangversion).version[0],
                "clang-cpp --help",
                "flang --help",
                "llvm-config --cxxflags",
            ]
            super(EB_AOCC, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands)
    
        def make_module_extra(self):
            """Custom variables for AOCC module."""
            txt = super(EB_AOCC, 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)
            # setting the AOCChome path
            txt += self.module_generator.set_environment('AOCChome', self.installdir)
            return txt
    
        def make_module_req_guess(self):
            """
            A dictionary of possible directories to look for.
            Include C_INCLUDE_PATH and CPLUS_INCLUDE_PATH as an addition to default ones
            """
            guesses = super(EB_AOCC, self).make_module_req_guess()
            guesses['C_INCLUDE_PATH'] = ['include']
            guesses['CPLUS_INCLUDE_PATH'] = ['include']
            return guesses