5050HOST_INJECTIONS_LOCATION = "/cvmfs/software.eessi.io/host_injections/"
5151
5252# Make sure a single environment variable name is used for this throughout the hooks
53+ EESSI_IGNORE_A64FX_RUST1650_ENVVAR = "EESSI_IGNORE_LMOD_ERROR_A64FX_RUST1650"
5354EESSI_IGNORE_ZEN4_GCC1220_ENVVAR = "EESSI_IGNORE_LMOD_ERROR_ZEN4_GCC1220"
5455
5556STACK_REPROD_SUBDIR = 'reprod'
6768}
6869
6970
71+ # Ensure that we don't print any messages in --terse mode
72+ # Note that --terse was introduced in EB 4.9.1
73+ orig_print_msg = print_msg
74+ orig_print_warning = print_warning
75+
76+ def print_msg (* args , ** kwargs ):
77+ if EASYBUILD_VERSION < '4.9.1' or not build_option ('terse' ):
78+ orig_print_msg (* args , ** kwargs )
79+
80+
81+ def print_warning (* args , ** kwargs ):
82+ if EASYBUILD_VERSION < '4.9.1' or not build_option ('terse' ):
83+ orig_print_warning (* args , ** kwargs )
84+
85+
7086def is_gcccore_1220_based (** kwargs ):
7187# ecname, ecversion, tcname, tcversion):
7288 """
@@ -140,6 +156,9 @@ def parse_hook(ec, *args, **kwargs):
140156 if cpu_target == CPU_TARGET_ZEN4 :
141157 parse_hook_zen4_module_only (ec , eprefix )
142158
159+ # All A64FX builds for the 2022b toolchain should use a newer Rust version, as the original one does not work
160+ parse_hook_bump_rust_version_in_2022b_for_a64fx (ec , eprefix )
161+
143162 # inject the GPU property (if required)
144163 ec = inject_gpu_property (ec )
145164
@@ -174,7 +193,10 @@ def verify_toolchains_supported_by_eessi_version(easyconfigs):
174193 site_top_level_toolchains_envvar = 'EESSI_SITE_TOP_LEVEL_TOOLCHAINS_' + eessi_version .replace ('.' , '_' )
175194 site_top_level_toolchains = parse_list_of_dicts_env (site_top_level_toolchains_envvar )
176195 for top_level_toolchain in EESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINS [eessi_version ] + site_top_level_toolchains :
177- supported_eessi_toolchains += get_toolchain_hierarchy (top_level_toolchain )
196+ try :
197+ supported_eessi_toolchains += get_toolchain_hierarchy (top_level_toolchain )
198+ except EasyBuildError as error :
199+ print_msg (f"No toolchain hierarchy found for { top_level_toolchain } , ignoring! ({ error } )" )
178200 for ec in easyconfigs :
179201 toolchain = ec ['ec' ]['toolchain' ]
180202 # if it is a system toolchain or appears in the list, we are all good
@@ -431,6 +453,33 @@ def parse_hook_openblas_relax_lapack_tests_num_errors(ec, eprefix):
431453 raise EasyBuildError ("OpenBLAS-specific hook triggered for non-OpenBLAS easyconfig?!" )
432454
433455
456+ def parse_hook_bump_rust_version_in_2022b_for_a64fx (ec , eprefix ):
457+ """
458+ Replace Rust 1.65.0 build dependency by version 1.75.0 for A64FX builds,
459+ because version 1.65.0 has build issues.
460+ """
461+ cpu_target = get_eessi_envvar ('EESSI_SOFTWARE_SUBDIR' )
462+
463+ if cpu_target == CPU_TARGET_A64FX :
464+ # For Rust 1.65.0 itself we inject an error message in the module file
465+ if ec ['name' ] == 'Rust' and ec ['version' ] == '1.65.0' :
466+ errmsg = "Rust 1.65.0 is not supported on A64FX. Please use version 1.75.0 instead."
467+ ec ['modluafooter' ] = 'if (not os.getenv("%s")) then LmodError("%s") end' % (EESSI_IGNORE_A64FX_RUST1650_ENVVAR , errmsg )
468+
469+ # For any builds that have a build dependency on Rust 1.65.0, we bump the version to 1.75.0
470+ if is_gcccore_1220_based (ecname = ec ['name' ], ecversion = ec ['version' ],
471+ tcname = ec ['toolchain' ]['name' ], tcversion = ec ['toolchain' ]['version' ]):
472+
473+ build_deps = ec ['builddependencies' ]
474+ rust_name = 'Rust'
475+ rust_original_version = '1.65.0'
476+ rust_new_version = '1.75.0'
477+ for idx , build_dep in enumerate (build_deps ):
478+ if build_dep [0 ] == rust_name and build_dep [1 ] == rust_original_version :
479+ build_deps [idx ] = (rust_name , rust_new_version )
480+ break
481+
482+
434483def parse_hook_pybind11_replace_catch2 (ec , eprefix ):
435484 """
436485 Replace Catch2 build dependency in pybind11 easyconfigs with one that doesn't use system toolchain.
@@ -513,9 +562,7 @@ def pre_fetch_hook(self, *args, **kwargs):
513562 PRE_FETCH_HOOKS [ec .name ](self , * args , ** kwargs )
514563
515564 # Always trigger this one, regardless of self.name
516- cpu_target = get_eessi_envvar ('EESSI_SOFTWARE_SUBDIR' )
517- if cpu_target == CPU_TARGET_ZEN4 :
518- pre_fetch_hook_zen4_gcccore1220 (self , * args , ** kwargs )
565+ pre_fetch_hook_unsupported_modules (self , * args , ** kwargs )
519566
520567 # Always check the software installation path
521568 pre_fetch_hook_check_installation_path (self , * args , ** kwargs )
@@ -549,13 +596,28 @@ def pre_fetch_hook_check_installation_path(self, *args, **kwargs):
549596 )
550597
551598
552- def pre_fetch_hook_zen4_gcccore1220 (self , * args , ** kwargs ):
553- """Use --force --module-only if building a foss-2022b-based EasyConfig for Zen4.
554- This toolchain will not be supported on Zen4, so we will generate a modulefile
555- and have it print an LmodError.
599+ def is_unsupported_module (ec ):
556600 """
557- if is_gcccore_1220_based (ecname = self .name , ecversion = self .version , tcname = self .toolchain .name ,
558- tcversion = self .toolchain .version ):
601+ Determine if the given module is unsupported in EESSI, and hence if a dummy module needs to be built that just prints an LmodError.
602+ If true, this function returns the name of the environment variable that can be used to ignore that particular LmodError,
603+ as this is still required to actually build the module itself (EasyBuild will load/test the module).
604+ Otherwise, it returns False.
605+ """
606+ cpu_target = get_eessi_envvar ('EESSI_SOFTWARE_SUBDIR' )
607+
608+ if cpu_target == CPU_TARGET_A64FX and ec .name == 'Rust' and ec .version == '1.65.0' :
609+ return EESSI_IGNORE_A64FX_RUST1650_ENVVAR
610+ if cpu_target == CPU_TARGET_ZEN4 and is_gcccore_1220_based (ecname = ec .name , ecversion = ec .version , tcname = ec .toolchain .name , tcversion = ec .toolchain .version ):
611+ return EESSI_IGNORE_ZEN4_GCC1220_ENVVAR
612+ return False
613+
614+
615+ def pre_fetch_hook_unsupported_modules (self , * args , ** kwargs ):
616+ """Use --force --module-only if building a module for unsupported software,
617+ e.g. foss-2022b-based EasyConfigs for Zen4.
618+ We will generate a modulefile and have it print an LmodError.
619+ """
620+ if is_unsupported_module (self ):
559621 if hasattr (self , EESSI_MODULE_ONLY_ATTR ):
560622 raise EasyBuildError ("'self' already has attribute %s! Can't use pre_fetch hook." ,
561623 EESSI_MODULE_ONLY_ATTR )
@@ -571,20 +633,20 @@ def pre_fetch_hook_zen4_gcccore1220(self, *args, **kwargs):
571633 print_msg ("Updated build option 'force' to 'True'" )
572634
573635
574- def pre_module_hook_zen4_gcccore1220 (self , * args , ** kwargs ):
636+ def pre_module_hook_unsupported_module (self , * args , ** kwargs ):
575637 """Make module load-able during module step"""
576- if is_gcccore_1220_based ( ecname = self . name , ecversion = self . version , tcname = self . toolchain . name ,
577- tcversion = self . toolchain . version ) :
638+ ignore_lmoderror_envvar = is_unsupported_module ( self )
639+ if ignore_lmoderror_envvar :
578640 if hasattr (self , 'initial_environ' ):
579641 # Allow the module to be loaded in the module step (which uses initial environment)
580- print_msg (f"Setting { EESSI_IGNORE_ZEN4_GCC1220_ENVVAR } in initial environment" )
581- self .initial_environ [EESSI_IGNORE_ZEN4_GCC1220_ENVVAR ] = "1"
642+ print_msg (f"Setting { ignore_lmoderror_envvar } in initial environment" )
643+ self .initial_environ [ignore_lmoderror_envvar ] = "1"
582644
583645
584- def post_module_hook_zen4_gcccore1220 (self , * args , ** kwargs ):
585- """Revert changes from pre_fetch_hook_zen4_gcccore1220 """
586- if is_gcccore_1220_based ( ecname = self . name , ecversion = self . version , tcname = self . toolchain . name ,
587- tcversion = self . toolchain . version ) :
646+ def post_module_hook_unsupported_module (self , * args , ** kwargs ):
647+ """Revert changes from pre_fetch_hook_unsupported_modules """
648+ ignore_lmoderror_envvar = is_unsupported_module ( self )
649+ if ignore_lmoderror_envvar :
588650 if hasattr (self , EESSI_MODULE_ONLY_ATTR ):
589651 update_build_option ('module_only' , getattr (self , EESSI_MODULE_ONLY_ATTR ))
590652 print_msg ("Restored original build option 'module_only' to %s" % getattr (self , EESSI_MODULE_ONLY_ATTR ))
@@ -601,9 +663,9 @@ def post_module_hook_zen4_gcccore1220(self, *args, **kwargs):
601663
602664 # If the variable to allow loading is set, remove it
603665 if hasattr (self , 'initial_environ' ):
604- if self .initial_environ .get (EESSI_IGNORE_ZEN4_GCC1220_ENVVAR , False ):
605- print_msg (f"Removing { EESSI_IGNORE_ZEN4_GCC1220_ENVVAR } in initial environment" )
606- del self .initial_environ [EESSI_IGNORE_ZEN4_GCC1220_ENVVAR ]
666+ if self .initial_environ .get (ignore_lmoderror_envvar , False ):
667+ print_msg (f"Removing { ignore_lmoderror_envvar } in initial environment" )
668+ del self .initial_environ [ignore_lmoderror_envvar ]
607669
608670
609671def post_easyblock_hook_copy_easybuild_subdir (self , * args , ** kwargs ):
@@ -679,6 +741,11 @@ def pre_configure_hook(self, *args, **kwargs):
679741 if self .name in PRE_CONFIGURE_HOOKS :
680742 PRE_CONFIGURE_HOOKS [self .name ](self , * args , ** kwargs )
681743
744+ # workaround for a Zlib macro being renamed in Gentoo, see https://bugs.gentoo.org/383179
745+ # (solves "expected initializer before 'OF'" errors)
746+ if self .name in ['FreeXL' , 'libspatialite' , 'VSEARCH' ]:
747+ self .cfg .update ('configopts' , 'CPPFLAGS="-DOF=_Z_OF ${CPPFLAGS}"' )
748+
682749
683750def pre_configure_hook_BLIS_a64fx (self , * args , ** kwargs ):
684751 """
@@ -740,18 +807,6 @@ def pre_configure_hook_score_p(self, *args, **kwargs):
740807 raise EasyBuildError ("Score-P-specific hook triggered for non-Score-P easyconfig?!" )
741808
742809
743- def pre_configure_hook_vsearch (self , * args , ** kwargs ):
744- """
745- Pre-configure hook for VSEARCH
746- - Workaround for a Zlib macro being renamed in Gentoo, see https://bugs.gentoo.org/383179
747- (solves "expected initializer before 'OF'" errors)
748- """
749- if self .name == 'VSEARCH' :
750- self .cfg .update ('configopts' , 'CPPFLAGS="-DOF=_Z_OF ${CPPFLAGS}"' )
751- else :
752- raise EasyBuildError ("VSEARCH-specific hook triggered for non-VSEARCH easyconfig?!" )
753-
754-
755810def pre_configure_hook_extrae (self , * args , ** kwargs ):
756811 """
757812 Pre-configure hook for Extrae
@@ -879,6 +934,42 @@ def pre_configure_hook_openblas_optarch_generic(self, *args, **kwargs):
879934 raise EasyBuildError ("OpenBLAS-specific hook triggered for non-OpenBLAS easyconfig?!" )
880935
881936
937+ def pre_configure_hook_openmpi_ipv6 (self , * args , ** kwargs ):
938+ """
939+ Pre-configure hook to enable IPv6 support in OpenMPI from EESSI 2025.06 onwards
940+ """
941+ if self .name == 'OpenMPI' :
942+ eessi_version = get_eessi_envvar ('EESSI_VERSION' )
943+ if eessi_version and LooseVersion (eessi_version ) >= '2025.06' :
944+ self .cfg .update ('configopts' , '--enable-ipv6' )
945+ else :
946+ raise EasyBuildError ("OpenMPI-specific hook triggered for non-OpenMPI easyconfig?!" )
947+
948+
949+ def pre_configure_hook_pmix_ipv6 (self , * args , ** kwargs ):
950+ """
951+ Pre-configure hook to enable IPv6 support in PMIx from EESSI 2025.06 onwards
952+ """
953+ if self .name == 'PMIx' :
954+ eessi_version = get_eessi_envvar ('EESSI_VERSION' )
955+ if eessi_version and LooseVersion (eessi_version ) >= '2025.06' :
956+ self .cfg .update ('configopts' , '--enable-ipv6' )
957+ else :
958+ raise EasyBuildError ("PMIx-specific hook triggered for non-PMIx easyconfig?!" )
959+
960+
961+ def pre_configure_hook_prrte_ipv6 (self , * args , ** kwargs ):
962+ """
963+ Pre-configure hook to enable IPv6 support in PRRTE from EESSI 2025.06 onwards
964+ """
965+ if self .name == 'PRRTE' :
966+ eessi_version = get_eessi_envvar ('EESSI_VERSION' )
967+ if eessi_version and LooseVersion (eessi_version ) >= '2025.06' :
968+ self .cfg .update ('configopts' , '--enable-ipv6' )
969+ else :
970+ raise EasyBuildError ("PRRTE-specific hook triggered for non-PRRTE easyconfig?!" )
971+
972+
882973def pre_configure_hook_libfabric_disable_psm3_x86_64_generic (self , * args , ** kwargs ):
883974 """Add --disable-psm3 to libfabric configure options when building with --optarch=GENERIC on x86_64."""
884975 if self .name == 'libfabric' :
@@ -926,23 +1017,66 @@ def pre_configure_hook_wrf_aarch64(self, *args, **kwargs):
9261017 raise EasyBuildError ("WRF-specific hook triggered for non-WRF easyconfig?!" )
9271018
9281019
929- def pre_configure_hook_LAMMPS_zen4 (self , * args , ** kwargs ):
1020+ def pre_configure_hook_LAMMPS_zen4_and_aarch64_cuda (self , * args , ** kwargs ):
9301021 """
9311022 pre-configure hook for LAMMPS:
932- - set kokkos_arch on x86_64/amd/zen4
1023+ - set kokkos_arch on x86_64/amd/zen4 and aarch64/nvidia/grace
1024+ - Disable SIMD for Aarch64 + cuda builds
9331025 """
9341026
1027+ # Get cpu_target for zen4 hook
9351028 cpu_target = get_eessi_envvar ('EESSI_SOFTWARE_SUBDIR' )
1029+
9361030 if self .name == 'LAMMPS' :
1031+ # Set kokkos_arch for LAMMPS version which do not have support for the target architecture
1032+ # This is no longer required with easybuild 5.1.2
9371033 if self .version in ('2Aug2023_update2' , '2Aug2023_update4' , '29Aug2024' ):
9381034 if get_cpu_architecture () == X86_64 :
9391035 if cpu_target == CPU_TARGET_ZEN4 :
9401036 # There is no support for ZEN4 in LAMMPS yet so falling back to ZEN3
9411037 self .cfg ['kokkos_arch' ] = 'ZEN3'
1038+ elif get_cpu_architecture () == AARCH64 :
1039+ if cpu_target == CPU_TARGET_NVIDIA_GRACE :
1040+ # There is no support for NVIDA grace in LAMMPS yet so falling back to ARMV81
1041+ self .cfg ['kokkos_arch' ] = 'ARMV81'
1042+
1043+ # Disable SIMD for specific CUDA versions
1044+ if self .version == '2Aug2023_update2' :
1045+ if get_cpu_architecture () == AARCH64 :
1046+ if self .cuda :
1047+ for dep in self .cfg .dependencies ():
1048+ if 'CUDA' == dep ['name' ]:
1049+ # This was broken until CUDA 13.1
1050+ if dep ['version' ] < LooseVersion ('13.1' ):
1051+ self .cfg ['kokkos_arch' ] = 'ARMV70'
1052+ cxxflags = os .getenv ('CXXFLAGS' , '' )
1053+ cxxflags = cxxflags .replace ('-mcpu=native' , '' )
1054+ cxxflags += ' -march=armv8-a+nosimd'
1055+ self .log .info ("Setting CXXFLAGS to disable NEON: %s" , cxxflags )
1056+ env .setvar ('CXXFLAGS' , cxxflags )
1057+
9421058 else :
9431059 raise EasyBuildError ("LAMMPS-specific hook triggered for non-LAMMPS easyconfig?!" )
9441060
9451061
1062+ def pre_configure_hook_cmake_system (self , * args , ** kwargs ):
1063+ """
1064+ pre-configure hook for CMake built with SYSTEM toolchain:
1065+ - remove configure options that link to ncurses static libraries for CMake with system toolchain;
1066+ see also https://github.com/EESSI/software-layer/issues/1175
1067+ """
1068+
1069+ if self .name == 'CMake' :
1070+ if is_system_toolchain (self .toolchain .name ):
1071+ self .log .info ("Removing configure options that require ncurses static libraries..." )
1072+ self .log .info (f"Original configopts value: { self .cfg ['configopts' ]} " )
1073+ regex = re .compile (r"-DCURSES_[A-Z]+_LIBRARY=\$EBROOTNCURSES/lib/lib[a-z]+\.a" )
1074+ self .cfg ['configopts' ] = regex .sub (self .cfg ['configopts' ], '' )
1075+ self .log .info (f"Updated configopts value: { self .cfg ['configopts' ]} " )
1076+ else :
1077+ raise EasyBuildError ("CMake-specific hook triggered for non-CMake easyconfig?!" )
1078+
1079+
9461080def pre_test_hook (self , * args , ** kwargs ):
9471081 """Main pre-test hook: trigger custom functions based on software name."""
9481082 if self .name in PRE_TEST_HOOKS :
@@ -1412,9 +1546,7 @@ def pre_module_hook(self, *args, **kwargs):
14121546 PRE_MODULE_HOOKS [self .name ](self , * args , ** kwargs )
14131547
14141548 # Always trigger this one, regardless of self.name
1415- cpu_target = get_eessi_envvar ('EESSI_SOFTWARE_SUBDIR' )
1416- if cpu_target == CPU_TARGET_ZEN4 :
1417- pre_module_hook_zen4_gcccore1220 (self , * args , ** kwargs )
1549+ pre_module_hook_unsupported_module (self , * args , ** kwargs )
14181550
14191551
14201552def post_module_hook (self , * args , ** kwargs ):
@@ -1423,9 +1555,7 @@ def post_module_hook(self, *args, **kwargs):
14231555 POST_MODULE_HOOKS [self .name ](self , * args , ** kwargs )
14241556
14251557 # Always trigger this one, regardless of self.name
1426- cpu_target = get_eessi_envvar ('EESSI_SOFTWARE_SUBDIR' )
1427- if cpu_target == CPU_TARGET_ZEN4 :
1428- post_module_hook_zen4_gcccore1220 (self , * args , ** kwargs )
1558+ post_module_hook_unsupported_module (self , * args , ** kwargs )
14291559
14301560
14311561# The post_easyblock_hook was introduced in EasyBuild 5.1.1.
@@ -1480,10 +1610,13 @@ def post_easyblock_hook(self, *args, **kwargs):
14801610 'ROCm-LLVM' : pre_configure_hook_llvm ,
14811611 'MetaBAT' : pre_configure_hook_metabat_filtered_zlib_dep ,
14821612 'OpenBLAS' : pre_configure_hook_openblas_optarch_generic ,
1613+ 'OpenMPI' : pre_configure_hook_openmpi_ipv6 ,
1614+ 'PMIx' : pre_configure_hook_pmix_ipv6 ,
1615+ 'PRRTE' : pre_configure_hook_prrte_ipv6 ,
14831616 'WRF' : pre_configure_hook_wrf_aarch64 ,
1484- 'LAMMPS' : pre_configure_hook_LAMMPS_zen4 ,
1617+ 'LAMMPS' : pre_configure_hook_LAMMPS_zen4_and_aarch64_cuda ,
14851618 'Score-P' : pre_configure_hook_score_p ,
1486- 'VSEARCH ' : pre_configure_hook_vsearch ,
1619+ 'CMake ' : pre_configure_hook_cmake_system ,
14871620}
14881621
14891622PRE_TEST_HOOKS = {
0 commit comments