Skip to content

Commit 21b0d82

Browse files
committed
Use EasyBuild hooks to limit toolchains that can be used for specific EESSI version
1 parent 793cb2f commit 21b0d82

File tree

2 files changed

+84
-20
lines changed

2 files changed

+84
-20
lines changed

.github/workflows/test-eb-hooks.yml

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,27 @@ jobs:
1313
matrix:
1414
EESSI_VERSION:
1515
- '2023.06'
16+
- '2025.06'
17+
include:
18+
# For each EESSI version we need to test different modules
19+
- EESSI_VERSION: '2023.06'
20+
COMPATIBLE_EASYCONFIG: 'M4-1.4.19-GCCcore-13.2.0.eb'
21+
INCOMPATIBLE_EASYCONFIG: 'M4-1.4.19-GCCcore-14.2.0.eb'
22+
- EESSI_VERSION: '2025.06'
23+
COMPATIBLE_EASYCONFIG: 'M4-1.4.19-GCCcore-14.2.0.eb'
24+
INCOMPATIBLE_EASYCONFIG: 'M4-1.4.19-GCCcore-13.2.0.eb'
25+
1626
steps:
1727
- name: Check out software-layer repository
1828
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
1929
with:
2030
fetch-depth: 0 # Fetch all history for all branches and tags
2131

22-
- name: Show host system info
23-
run: |
24-
echo "/proc/cpuinfo:"
25-
cat /proc/cpuinfo
26-
echo
27-
echo "lscpu:"
28-
lscpu
29-
30-
- name: Mount EESSI CernVM-FS pilot repository
31-
uses: cvmfs-contrib/github-action-cvmfs@55899ca74cf78ab874bdf47f5a804e47c198743c # v4.0
32+
- name: Mount EESSI CernVM-FS repository
33+
uses: eessi/github-action-eessi@v3
3234
with:
33-
cvmfs_config_package: https://github.com/EESSI/filesystem-layer/releases/download/latest/cvmfs-config-eessi_latest_all.deb
34-
cvmfs_http_proxy: DIRECT
35-
cvmfs_repositories: software.eessi.io
35+
eessi_stack_version: ${{matrix.EESSI_VERSION}}
36+
use_eessi_module: true
3637

3738
- name: Check that EasyBuild hook is up to date
3839
if: ${{ github.event_name == 'pull_request' }}
@@ -56,6 +57,24 @@ jobs:
5657
sed -i "s/<EESSI_VERSION>/${{matrix.EESSI_VERSION}}/g" "${TEMP_FILE}"
5758
5859
# Compare the hooks to what is shipped in the repository
59-
source /cvmfs/software.eessi.io/versions/${{matrix.EESSI_VERSION}}/init/bash
6060
module load EESSI-extend
6161
diff "$TEMP_FILE" "$EASYBUILD_HOOKS"
62+
63+
- name: Test that toolchain verification check works
64+
if: ${{ github.event_name == 'pull_request' }}
65+
run: |
66+
# Set up some environment variables
67+
export COMPATIBLE_EASYCONFIG=${{matrix.COMPATIBLE_EASYCONFIG}}
68+
export INCOMPATIBLE_EASYCONFIG=${{matrix.INCOMPATIBLE_EASYCONFIG}}
69+
70+
# Load specific EESSI-extend vertsion (proxies a version check)
71+
module load EESSI-extend/${{matrix.EESSI_VERSION}}-easybuild
72+
73+
# Test an easyconfig that should work
74+
eb --hooks=$PWD/eb_hooks.py "$COMPATIBLE_EASYCONFIG" --stop fetch
75+
76+
# Pick an outdated toolchain for the negative test
77+
eb --hooks=$PWD/eb_hooks.py "$INCOMPATIBLE_EASYCONFIG" --stop fetch 2>&1 1>/dev/null | grep -q "not supported in EESSI"
78+
79+
# Check the override works
80+
EESSI_OVERRIDE_TOOLCHAIN_CHECK=1 eb --hooks=$PWD/eb_hooks.py "$INCOMPATIBLE_EASYCONFIG" --stop fetch

eb_hooks.py

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import easybuild.tools.environment as env
99
from easybuild.easyblocks.generic.configuremake import obtain_config_guess
1010
from easybuild.framework.easyconfig.constants import EASYCONFIG_CONSTANTS
11+
from easybuild.framework.easyconfig.easyconfig import get_toolchain_hierarchy
1112
from easybuild.tools import config
1213
from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning
1314
from easybuild.tools.config import build_option, install_path, update_build_option
1415
from easybuild.tools.filetools import apply_regex_substitutions, copy_dir, copy_file, remove_file, symlink, which
1516
from easybuild.tools.run import run_cmd
1617
from easybuild.tools.systemtools import AARCH64, POWER, X86_64, get_cpu_architecture, get_cpu_features
1718
from easybuild.tools.toolchain.compiler import OPTARCH_GENERIC
19+
from easybuild.tools.toolchain.toolchain import is_system_toolchain
1820
from easybuild.tools.version import VERSION as EASYBUILD_VERSION
1921
from easybuild.tools.modules import get_software_root_env_var_name
2022

@@ -50,6 +52,18 @@
5052

5153
STACK_REPROD_SUBDIR = 'reprod'
5254

55+
EESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINS = {
56+
'2023.06': [
57+
{'name': 'foss', 'version': '2022b'},
58+
{'name': 'foss', 'version': '2023a'},
59+
{'name': 'foss', 'version': '2023b'},
60+
],
61+
'2025.06': [
62+
{'name': 'foss', 'version': '2024a'},
63+
{'name': 'foss', 'version': '2025a'},
64+
],
65+
}
66+
5367

5468
def is_gcccore_1220_based(**kwargs):
5569
# ecname, ecversion, tcname, tcversion):
@@ -128,14 +142,45 @@ def parse_hook(ec, *args, **kwargs):
128142
ec = inject_gpu_property(ec)
129143

130144

145+
def verify_toolchains_supported_by_eessi_version(easyconfigs):
146+
"""Each EESSI version supports a limited set of toolchains, sanity check the easyconfigs for toolchain support."""
147+
eessi_version = get_eessi_envvar('EESSI_VERSION')
148+
supported_eessi_toolchains = []
149+
for top_level_toolchain in EESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINS[eessi_version]:
150+
supported_eessi_toolchains += get_toolchain_hierarchy(top_level_toolchain)
151+
for ec in easyconfigs:
152+
toolchain = ec['ec']['toolchain']
153+
# if it is a system toolchain or appears in the list, we are all good
154+
if is_system_toolchain(toolchain['name']):
155+
continue
156+
elif not any(toolchain.items() <= supported.items() for supported in supported_eessi_toolchains):
157+
raise EasyBuildError(
158+
f"Toolchain {toolchain} (required by {ec['full_mod_name']}) is not supported in EESSI/{eessi_version}\n"
159+
f"Supported toolchains are:\n" + "\n".join(sorted(" " + str(tc) for tc in supported_eessi_toolchains))
160+
)
161+
162+
163+
def pre_build_and_install_loop_hook(easyconfigs):
164+
"""Main pre_build_and_install_loop hook: trigger custom functions before beginning installation loop."""
165+
166+
# Always check that toolchain supported by the EESSI version (unless overridden)
167+
if os.getenv("EESSI_OVERRIDE_TOOLCHAIN_CHECK"):
168+
print_warning("Overriding the check that the toolchains are supported by the EESSI version.")
169+
else:
170+
verify_toolchains_supported_by_eessi_version(easyconfigs)
171+
172+
131173
def post_ready_hook(self, *args, **kwargs):
132174
"""
133175
Post-ready hook: limit parallellism for selected builds based on software name and CPU target.
134176
parallelism needs to be limited because some builds require a lot of memory per used core.
135177
"""
136178
# 'parallel' easyconfig parameter (EB4) or the parallel property (EB5) is set via EasyBlock.set_parallel
137179
# in ready step based on available cores
138-
parallel = getattr(self, 'parallel', self.cfg['parallel'])
180+
if hasattr(self, 'parallel'):
181+
parallel = self.parallel
182+
else:
183+
parallel = self.cfg['parallel']
139184

140185
if parallel == 1:
141186
return # no need to limit if already using 1 core
@@ -167,8 +212,8 @@ def post_ready_hook(self, *args, **kwargs):
167212

168213
# apply the limit if it's different from current
169214
if new_parallel != parallel:
170-
if EASYBUILD_VERSION >= '5':
171-
self.cfg.parallel = new_parallel
215+
if hasattr(self, 'parallel'):
216+
self.parallel = new_parallel
172217
else:
173218
self.cfg['parallel'] = new_parallel
174219
msg = "limiting parallelism to %s (was %s) for %s on %s to avoid out-of-memory failures during building/testing"
@@ -394,7 +439,7 @@ def parse_hook_freeimage_aarch64(ec, *args, **kwargs):
394439
https://github.com/EESSI/software-layer/pull/736#issuecomment-2373261889
395440
"""
396441
if ec.name == 'FreeImage' and ec.version in ('3.18.0',):
397-
if os.getenv('EESSI_CPU_FAMILY') == 'aarch64':
442+
if get_eessi_envvar('EESSI_CPU_FAMILY') == 'aarch64':
398443
# Make sure the toolchainopts key exists, and the value is a dict,
399444
# before we add the option to enable PIC and disable PNG_ARM_NEON_OPT
400445
if 'toolchainopts' not in ec or ec['toolchainopts'] is None:
@@ -1230,7 +1275,7 @@ def replace_non_distributable_files_with_symlinks(log, install_dir, pkg_name, al
12301275
# CUDA and cu* libraries themselves don't care about compute capability so remove this
12311276
# duplication from under host_injections (symlink to a single CUDA or cu* library
12321277
# installation for all compute capabilities)
1233-
accel_subdir = os.getenv("EESSI_ACCELERATOR_TARGET")
1278+
accel_subdir = get_eessi_envvar("EESSI_ACCELERATOR_TARGET")
12341279
if accel_subdir:
12351280
host_inj_path = host_inj_path.replace("/accel/%s" % accel_subdir, '')
12361281
# make sure source and target of symlink are not the same
@@ -1326,7 +1371,7 @@ def post_easyblock_hook(self, *args, **kwargs):
13261371

13271372
# Always trigger this one for EESSI CVMFS/site installations and version 2025.06 or newer, regardless of self.name
13281373
if os.getenv('EESSI_CVMFS_INSTALL') or os.getenv('EESSI_SITE_INSTALL'):
1329-
if os.getenv('EESSI_VERSION') and LooseVersion(os.getenv('EESSI_VERSION')) >= '2025.06':
1374+
if get_eessi_envvar('EESSI_VERSION') and LooseVersion(get_eessi_envvar('EESSI_VERSION')) >= '2025.06':
13301375
post_easyblock_hook_copy_easybuild_subdir(self, *args, **kwargs)
13311376
else:
13321377
self.log.debug("No CVMFS/site installation requested, not running post_easyblock_hook_copy_easybuild_subdir.")

0 commit comments

Comments
 (0)