Skip to content
Snippets Groups Projects
intel_compilers.py 7.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • # #
    # Copyright 2021-2021 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 <http://www.gnu.org/licenses/>.
    # #
    """
    EasyBuild support for installing Intel compilers, implemented as an easyblock
    
    @author: Kenneth Hoste (Ghent University)
    """
    import os
    from distutils.version import LooseVersion
    
    from easybuild.easyblocks.generic.intelbase import IntelBase
    from easybuild.easyblocks.t.tbb import get_tbb_gccprefix
    from easybuild.tools.build_log import EasyBuildError, print_msg
    from easybuild.tools.run import run_cmd
    
    
    class EB_intel_minus_compilers(IntelBase):
        """
        Support for installing Intel compilers, starting with verion 2021.x (oneAPI)
        """
    
        def __init__(self, *args, **kwargs):
            """
            Easyblock constructor: check version
            """
            super(EB_intel_minus_compilers, self).__init__(*args, **kwargs)
    
            # this easyblock is only valid for recent versions of the Intel compilers (2021.x, oneAPI)
            if LooseVersion(self.version) < LooseVersion('2021'):
                raise EasyBuildError("Invalid version %s, should be >= 2021.x" % self.version)
    
            self.compilers_subdir = os.path.join('compiler', self.version, 'linux')
            # note that tbb may have a lower version number than the compiler, so use 'latest' symlink
            # for example compiler 2021.1.2 has tbb 2021.1.1.
            self.tbb_subdir = os.path.join('tbb', 'latest')
    
        def prepare_step(self, *args, **kwargs):
            """
            Prepare environment for installing.
    
            Specify that oneAPI versions of Intel compilers don't require a runtime license.
            """
            # avoid that IntelBase trips over not having license info specified
            kwargs['requires_runtime_license'] = False
    
            super(EB_intel_minus_compilers, self).prepare_step(*args, **kwargs)
    
        def configure_step(self):
            """Configure installation."""
    
            # redefine $HOME for install step, to avoid that anything is stored in $HOME/intel
            # (like the 'installercache' database)
            self.cfg['preinstallopts'] += " HOME=%s " % self.builddir
    
        def install_step(self):
            """
            Install step: install each 'source file' one by one.
            Installing the Intel compilers could be done via a single installation file (HPC Toolkit),
            or with separate installation files (patch releases of the C++ and Fortran compilers).
            """
            srcs = self.src[:]
            cnt = len(srcs)
            for idx, src in enumerate(srcs):
                print_msg("installing part %d/%s (%s)..." % (idx + 1, cnt, src['name']))
                self.src = [src]
                super(EB_intel_minus_compilers, self).install_step()
    
        def sanity_check_step(self):
            """
            Custom sanity check for Intel compilers.
            """
    
            classic_compiler_cmds = ['icc', 'icpc', 'ifort']
            oneapi_compiler_cmds = [
                'dpcpp',  # Intel oneAPI Data Parallel C++ compiler
                'icx',  # oneAPI Intel C compiler
                'icpx',  # oneAPI Intel C++ compiler
                'ifx',  # oneAPI Intel Fortran compiler
            ]
            bindir = os.path.join(self.compilers_subdir, 'bin')
            classic_compiler_paths = [os.path.join(bindir, x) for x in oneapi_compiler_cmds]
            oneapi_compiler_paths = [os.path.join(bindir, 'intel64', x) for x in classic_compiler_cmds]
    
            custom_paths = {
                'files': classic_compiler_paths + oneapi_compiler_paths,
                'dirs': [self.compilers_subdir],
            }
    
            all_compiler_cmds = classic_compiler_cmds + oneapi_compiler_cmds
            custom_commands = ["which %s" % c for c in all_compiler_cmds]
    
            # only for 2021.x versions do all compiler commands have the expected version;
            # for example: for 2022.0.1, icc has version 2021.5.0, icpx has 2022.0.0
            if LooseVersion(self.version) >= LooseVersion('2022.0'):
                custom_commands.extend("%s --version" % c for c in all_compiler_cmds)
            else:
                custom_commands.extend("%s --version | grep %s" % (c, self.version) for c in all_compiler_cmds)
    
            super(EB_intel_minus_compilers, self).sanity_check_step(custom_paths=custom_paths,
                                                                    custom_commands=custom_commands)
    
        def make_module_req_guess(self):
            """
            Paths to consider for prepend-paths statements in module file
            """
            libdirs = [
                'lib',
                os.path.join('lib', 'x64'),
                os.path.join('compiler', 'lib', 'intel64_lin'),
            ]
            libdirs = [os.path.join(self.compilers_subdir, x) for x in libdirs]
            # resolve 'latest' symlink for tbb (if module guess is run with install in place)
            if os.path.islink(os.path.join(self.installdir, self.tbb_subdir)):
                tbb_version = os.readlink(os.path.join(self.installdir, self.tbb_subdir))
            else:
                tbb_version = 'latest'
            tbb_subdir = os.path.join('tbb', tbb_version)
            tbb_libsubdir = os.path.join(tbb_subdir, 'lib', 'intel64')
            libdirs.append(os.path.join(tbb_libsubdir,
                                        get_tbb_gccprefix(os.path.join(self.installdir, tbb_libsubdir))))
            guesses = {
                'PATH': [
                    os.path.join(self.compilers_subdir, 'bin'),
                    os.path.join(self.compilers_subdir, 'bin', 'intel64'),
                ],
                'LD_LIBRARY_PATH': libdirs,
                'LIBRARY_PATH': libdirs,
                'OCL_ICD_FILENAMES': [
                    os.path.join(self.compilers_subdir, 'lib', 'x64', 'libintelocl.so'),
                ],
                'CPATH': [
                    os.path.join(tbb_subdir, 'include'),
                ],
                'TBBROOT': [tbb_subdir],
            }
            return guesses
    
        def make_module_extra(self):
            """Additional custom variables for intel-compiler"""
            txt = super(EB_intel_minus_compilers, self).make_module_extra()
    
            # On Debian/Ubuntu, /usr/include/x86_64-linux-gnu, or whatever dir gcc uses, needs to be included
            # in $CPATH for Intel C compiler
            multiarch_out, ec = run_cmd("gcc -print-multiarch", simple=False)
            multiarch_out = multiarch_out.strip()
            if ec == 0 and multiarch_out:
                multiarch_inc_dir, ec = run_cmd("gcc -E -Wp,-v -xc /dev/null 2>&1 | grep %s$" % multiarch_out)
                if ec == 0 and multiarch_inc_dir:
                    multiarch_inc_dir = multiarch_inc_dir.strip()
                    self.log.info("Adding multiarch include path %s to $CPATH in generated module file", multiarch_inc_dir)
                    # system location must be appended at the end, so use append_paths
                    txt += self.module_generator.append_paths('CPATH', [multiarch_inc_dir], allow_abs=True)
    
            return txt