Skip to content

Commit

Permalink
Enhance AOCC EasyBlock to use config files for GCC toolchain when req…
Browse files Browse the repository at this point in the history
…uired

Starting with AOCC 5.0.0, creating wrappers for `clang[x]` compilers will
cause compile errors since `libstdc++` is not linked anymore. To work around
this, create config files for these compilers instead. Since `flang` is not
affected and doesn't understand config files, keep the old wrapping for this
compiler.

Also add a sanity check to ensure that the correct toolchain is selected.

Signed-off-by: Jan André Reuter <[email protected]>
  • Loading branch information
Thyre committed Oct 14, 2024
1 parent 3cd59c8 commit 779c20a
Showing 1 changed file with 116 additions and 31 deletions.
147 changes: 116 additions & 31 deletions easybuild/easyblocks/a/aocc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
@author: Sebastian Achilles (Forschungszentrum Juelich GmbH)
"""

import glob
import os
import re
import stat

from easybuild.tools import LooseVersion
Expand All @@ -40,13 +42,9 @@
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 "$@"
"""
from easybuild.tools.modules import get_software_root, get_software_version
from easybuild.tools.run import run_cmd
from easybuild.tools.systemtools import get_shared_lib_ext, get_cpu_architecture


class EB_AOCC(PackedBinary):
Expand All @@ -66,6 +64,7 @@ def __init__(self, *args, **kwargs):
super(EB_AOCC, self).__init__(*args, **kwargs)

self.clangversion = self.cfg['clangversion']
self.gcc_prefix = None

def _aocc_guess_clang_version(self):
map_aocc_to_clang_ver = {
Expand All @@ -76,6 +75,7 @@ def _aocc_guess_clang_version(self):
'4.0.0': '14.0.6',
'4.1.0': '16.0.3',
'4.2.0': '16.0.3',
'5.0.0': '17.0.6',
}

if self.version in map_aocc_to_clang_ver:
Expand All @@ -89,43 +89,27 @@ def _aocc_guess_clang_version(self):
]
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 _create_compiler_wrappers(self, compilers_to_wrap):
# Wrapper script definition
WRAPPER_TEMPLATE = """#!/bin/sh
def post_install_step(self):
"""Create wrappers for the compilers to make sure compilers picks up GCCcore as GCC toolchain"""
%(compiler_name)s --gcc-toolchain=$EBROOTGCCCORE "$@"
"""

orig_compiler_tmpl = '%s.orig'
orig_compiler_tmpl = '%s/%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})
write_file(wrapper_f, WRAPPER_TEMPLATE % {'compiler_name': orig_compiler_tmpl % (os.path.join(self.installdir, 'bin'), 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)
move_file(actual_compiler, orig_compiler_tmpl % (os.path.join(self.installdir, 'bin'), comp))
else:
err_str = "Tried to move '%s' to '%s', but it does not exist!"
raise EasyBuildError(err_str, actual_compiler, '%s.orig' % actual_compiler)
Expand All @@ -137,6 +121,105 @@ def create_wrapper(wrapper_comp):
err_str = "Creating wrapper for '%s' not possible, since original compiler was not renamed!"
raise EasyBuildError(err_str, actual_compiler)

def _create_compiler_config_files(self, compilers_to_add_config_file):
# For each of the compiler suites, add a .cfg file which points to the correct GCCcore as the GCC toolchain
bin_dir = os.path.join(self.installdir, 'bin')
prefix_str = '--gcc-install-dir=%s' % self.gcc_prefix
for comp in compilers_to_add_config_file:
with open(os.path.join(bin_dir, '%s.cfg' % comp), 'w') as f:
f.write(prefix_str)

def _sanity_check_gcc_prefix(self):
"""Check if the GCC prefix is correct."""
compilers_to_check = [
'clang',
'clang++',
'clang-%s' % LooseVersion(self.clangversion).version[0],
'clang-cpp',
'flang',
]

# Set prefix if not done during installation.
self._set_gcc_prefix()
rgx = re.compile('Selected GCC installation: (.*)')
for comp in compilers_to_check:
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 _set_gcc_prefix(self):
"""Set the GCC prefix for the build."""
if not self.gcc_prefix:
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)
self.gcc_prefix = os.path.abspath(matches[0])
self.log.debug("Using %s as the gcc install location", self.gcc_prefix)

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):
"""
For AOCC <5.0.0:
Create wrappers for the compilers to make sure compilers picks up GCCcore as GCC toolchain.
For AOCC >= 5.0.0:
Create [compiler-name].cfg files to point the compiler to the correct GCCcore as GCC toolchain.
For compilers not supporting this option, wrap the compilers using the old method.
"""
compilers_to_wrap = []
compilers_to_add_config_files = []

if LooseVersion(self.version) < LooseVersion("5.0.0"):
compilers_to_wrap += [
'clang',
'clang++',
'clang-%s' % LooseVersion(self.clangversion).version[0],
'clang-cpp',
'flang',
]
else:
self._set_gcc_prefix()
compilers_to_add_config_files += [
'clang',
'clang++',
'clang-cpp'
]
compilers_to_wrap += [
'flang'
]

self._create_compiler_config_files(compilers_to_add_config_files)
self._create_compiler_wrappers(compilers_to_wrap)
super(EB_AOCC, self).post_install_step()

def sanity_check_step(self):
Expand Down Expand Up @@ -167,6 +250,8 @@ def sanity_check_step(self):
"flang --help",
"llvm-config --cxxflags",
]

self._sanity_check_gcc_prefix()
super(EB_AOCC, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands)

def make_module_extra(self):
Expand Down

0 comments on commit 779c20a

Please sign in to comment.