@@ -362,6 +362,10 @@ def test_deps(self):
362362 def check_dep_vars (self , gen , dep , dep_vars ):
363363 """Check whether available variants of a particular dependency are acceptable or not."""
364364
365+ # short-circuit in case there's only one dependency variant, or none at all
366+ if len (dep_vars ) <= 1 :
367+ return True
368+
365369 # 'guilty' until proven 'innocent'
366370 res = False
367371
@@ -956,43 +960,104 @@ def get_deps_for(ec):
956960
957961 # some software also follows <year>{a,b} versioning scheme,
958962 # which throws off the pattern matching done below for toolchain versions
959- false_positives_regex = re .compile ('^MATLAB-Engine-20[0-9][0-9][ab]' )
963+ false_positives_regex = re .compile (r'^MATLAB(-Engine)?-20[0-9][0-9][ab]' )
964+
965+ # map GCC(core) version to toolchain generations;
966+ # only for recent generations, where we want to limit dependency variants as much as possible
967+ # across all easyconfigs of that generation (regardless of whether a full toolchain or subtoolchain is used);
968+ # see https://docs.easybuild.io/common-toolchains/#common_toolchains_overview
969+ gcc_tc_gen_map = {
970+ '6.4' : '2018a' ,
971+ '7.3' : '2018b' ,
972+ '8.2' : '2019a' ,
973+ '8.3' : '2019b' ,
974+ '9.3' : '2020a' ,
975+ '10.2' : '2020b' ,
976+ '10.3' : '2021a' ,
977+ '11.1' : None ,
978+ '11.2' : '2021b' ,
979+ '11.3' : '2022a' ,
980+ '11.4' : None ,
981+ '12.1' : None ,
982+ '12.2' : '2022b' ,
983+ '12.3' : '2023a' ,
984+ '13.1' : None ,
985+ '13.1' : None ,
986+ '13.2' : '2023b' ,
987+ '13.3' : '2024a' ,
988+ '14.1' : None ,
989+ '14.2' : '2025a' ,
990+ '14.3' : '2025b' ,
991+ '15.1' : None ,
992+ }
960993
961994 multi_dep_vars_msg = ''
962- # restrict to checking dependencies of easyconfigs using common toolchains (start with 2018a)
963- # and GCCcore subtoolchain for common toolchains, starting with GCCcore 7.x
964- for pattern in ['20(1[89]|[2-9][0-9])[ab]' , r'GCCcore-([7-9]|[1-9][0-9])\.[0-9]' ]:
965- all_deps = {}
966- regex = re .compile (r'^.*-(?P<tc_gen>%s).*\.eb$' % pattern )
995+ # restrict to checking dependencies of easyconfigs using common toolchains,
996+ # and GCCcore subtoolchain for common toolchains;
997+ # for now, we only enforce this for recent toolchain versions (2025a + GCCcore 14.x, and newer);
998+ patterns = [
999+ # compiler-only subtoolchains GCCcore and GCC
1000+ # r'GCCcore-[7-9]\.[0-9]\.',
1001+ # r'GCC(core)?-1[0-9]\.[0-9]\.', # GCCcore 10.x, etc.
1002+ r'GCC(core)?-1[4-9]\.[0-9]\.' , # GCCcore 14.x & newer
1003+ # only check GCC 9.x toolchains, not older GCC versions
1004+ # (we started checking dependency variants too late for GCC 8.x and older)
1005+ # r'GCC-9\.[0-9]\.',
1006+ # full toolchains, like foss/2019b or intel/2020a
1007+ # r'(201[89]|20[2-9][0-9])[ab]',
1008+ r'(202[5-9]|20[3-9][0-9])[ab]' , # 2025a and newer
1009+ ]
1010+
1011+ all_deps = {}
1012+
1013+ # collect variants for all dependencies of easyconfigs that use a toolchain that matches
1014+ for ec in self .ordered_specs :
1015+ ec_file = os .path .basename (ec ['spec' ])
1016+ ec_deps = None
9671017
968- # collect variants for all dependencies of easyconfigs that use a toolchain that matches
969- for ec in self .ordered_specs :
970- ec_file = os .path .basename (ec ['spec' ])
1018+ for pattern in patterns :
1019+ regex = re .compile (r'^.*-(?P<tc_gen>%s).*\.eb$' % pattern )
9711020
9721021 # take into account software which also follows a <year>{a,b} versioning scheme
9731022 ec_file = false_positives_regex .sub ('' , ec_file )
9741023
9751024 res = regex .match (ec_file )
9761025 if res :
9771026 tc_gen = res .group ('tc_gen' )
1027+
1028+ if tc_gen .startswith ('GCC' ):
1029+ gcc_ver = tc_gen .split ('-' , 1 )[1 ].rstrip ('.' )
1030+ if gcc_ver in gcc_tc_gen_map and gcc_tc_gen_map [gcc_ver ] is not None :
1031+ tc_gen = gcc_tc_gen_map [gcc_ver ]
1032+ elif LooseVersion (gcc_ver ) >= LooseVersion ('10.2' ) and gcc_ver not in gcc_tc_gen_map :
1033+ # for recent GCC versions, we really want to have a mapping in place...
1034+ self .fail ("No mapping for GCC(core) %s to toolchain generation!" % gcc_ver )
1035+
1036+ if ec_deps is None :
1037+ ec_deps = get_deps_for (ec )
1038+
9781039 all_deps_tc_gen = all_deps .setdefault (tc_gen , {})
979- for dep_name , dep_ver , dep_versuff , dep_mod_name in get_deps_for ( ec ) :
1040+ for dep_name , dep_ver , dep_versuff , _ in ec_deps :
9801041 dep_variants = all_deps_tc_gen .setdefault (dep_name , {})
9811042 # a variant is defined by version + versionsuffix
9821043 variant = "version: %s; versionsuffix: %s" % (dep_ver , dep_versuff )
9831044 # keep track of which easyconfig this is a dependency
9841045 dep_variants .setdefault (variant , set ()).add (ec_file )
9851046
986- # check which dependencies have more than 1 variant
987- for tc_gen , deps in sorted (all_deps .items ()):
988- for dep , dep_vars in sorted (deps .items ()):
989- if not self .check_dep_vars (tc_gen , dep , dep_vars ):
990- multi_dep_vars_msg += "Found %s variants of '%s' dependency " % (len (dep_vars ), dep )
991- multi_dep_vars_msg += "in easyconfigs using '%s' toolchain generation\n * " % tc_gen
992- multi_dep_vars_msg += '\n * ' .join ("%s as dep for %s" % v for v in sorted (dep_vars .items ()))
993- multi_dep_vars_msg += '\n '
1047+ # check which dependencies have more than 1 variant
1048+ multi_dep_vars , multi_dep_vars_msg = [], ''
1049+ for tc_gen in sorted (all_deps .keys ()):
1050+ for dep in sorted (all_deps [tc_gen ].keys ()):
1051+ dep_vars = all_deps [tc_gen ][dep ]
1052+ if not self .check_dep_vars (tc_gen , dep , dep_vars ):
1053+ multi_dep_vars .append (dep )
1054+ multi_dep_vars_msg += "\n found %s variants of '%s' dependency " % (len (dep_vars ), dep )
1055+ multi_dep_vars_msg += "in easyconfigs using '%s' toolchain generation\n * " % tc_gen
1056+ multi_dep_vars_msg += '\n * ' .join ("%s as dep for %s" % v for v in sorted (dep_vars .items ()))
1057+ multi_dep_vars_msg += '\n '
1058+
9941059 if multi_dep_vars_msg :
995- self .fail (' Should not have multiple variants of dependencies. \n ' + multi_dep_vars_msg )
1060+ self .fail (" Should not have multi-variant dependencies in easyconfigs: \n %s" % multi_dep_vars_msg )
9961061
9971062 def test_downloadable_or_instructions (self ):
9981063 """
0 commit comments