2020EESSI_REFERENCE_ARCHITECTURE = "x86_64/intel/icelake"
2121
2222# Give order to my toolchains so I can easily figure out what "latest" means
23- EESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINS = OrderedDict ({
24- '2025.06' : [
25- {'name' : 'foss' , 'version' : '2025a' },
26- {'name' : 'foss' , 'version' : '2024a' },
27- ],
28- '2023.06' : [
29- {'name' : 'foss' , 'version' : '2023b' },
30- {'name' : 'foss' , 'version' : '2023a' },
31- {'name' : 'foss' , 'version' : '2022b' },
32- ],
33- })
23+ EESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINS = OrderedDict (
24+ {
25+ "2025.06" : [
26+ {"name" : "foss" , "version" : "2025a" },
27+ {"name" : "foss" , "version" : "2024a" },
28+ ],
29+ "2023.06" : [
30+ {"name" : "foss" , "version" : "2023b" },
31+ {"name" : "foss" , "version" : "2023a" },
32+ {"name" : "foss" , "version" : "2022b" },
33+ ],
34+ }
35+ )
36+
3437
3538@contextmanager
3639def suppress_stdout ():
@@ -56,26 +59,17 @@ def load_and_list_modules(module_name):
5659 module --terse list 2>&1
5760 """
5861
59- result = subprocess .run (
60- ["bash" , "-c" , cmd ],
61- stdout = subprocess .PIPE ,
62- stderr = subprocess .STDOUT ,
63- text = True
64- )
62+ result = subprocess .run (["bash" , "-c" , cmd ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT , text = True )
6563
6664 if result .returncode != 0 :
6765 raise RuntimeError (f"Failed to load module '{ module_name } ':\n { result .stdout } " )
6866
6967 # Parse module list output
70- modules = [
71- line
72- for line in result .stdout .splitlines ()
73- if "/" in line
74- ]
75-
68+ modules = [line for line in result .stdout .splitlines () if "/" in line ]
69+
7670 # Filter out the modules we expect to be loaded
77- eessi_extend_module_stub = ' EESSI-extend/'
78- eb_module_stub = ' EasyBuild/'
71+ eessi_extend_module_stub = " EESSI-extend/"
72+ eb_module_stub = " EasyBuild/"
7973 if module_name .startswith (eessi_extend_module_stub ):
8074 # Don't filter anything
8175 pass
@@ -84,7 +78,11 @@ def load_and_list_modules(module_name):
8478 modules = [module for module in modules if not module .startswith (eessi_extend_module_stub )]
8579 else :
8680 # Filter EESSI-extend and EasyBuild
87- modules = [module for module in modules if not module .startswith (eessi_extend_module_stub ) and not module .startswith (eb_module_stub )]
81+ modules = [
82+ module
83+ for module in modules
84+ if not module .startswith (eessi_extend_module_stub ) and not module .startswith (eb_module_stub )
85+ ]
8886
8987 return modules
9088
@@ -96,23 +94,23 @@ def use_timestamped_reprod_if_exists(original_path):
9694 """
9795 # Default to returning the original path
9896 returned_path = original_path
99-
97+
10098 # Split path
10199 parts = original_path .strip (os .sep ).split (os .sep )
102100
103101 # Find the last occurrence of 'software'
104- idx = len (parts ) - 1 - parts [::- 1 ].index (' software' )
102+ idx = len (parts ) - 1 - parts [::- 1 ].index (" software" )
105103
106104 # Replace 'software' by 'reprod'
107- parts [idx ] = ' reprod'
105+ parts [idx ] = " reprod"
108106
109107 # Path up to version directory (software/software/version)
110- pre_timestamp = os .sep .join (['' ] + parts [:idx + 3 ])
108+ pre_timestamp = os .sep .join (["" ] + parts [: idx + 3 ])
111109 # Path after version directory (easybuild/reprod/easyblocks)
112- post_version = parts [idx + 3 :]
110+ post_version = parts [idx + 3 :]
113111
114112 # Look for timestamp directories under pre_timestamp
115- timestamp_dirs = [d for d in glob .glob (os .path .join (pre_timestamp , '*' )) if os .path .isdir (d )]
113+ timestamp_dirs = [d for d in glob .glob (os .path .join (pre_timestamp , "*" )) if os .path .isdir (d )]
116114 if timestamp_dirs :
117115 latest_timestamp = max (timestamp_dirs ) # lexicographic order
118116 # Reconstruct path: reprod/.../version/<latest_timestamp>/easybuild/reprod/easyblocks
@@ -122,48 +120,49 @@ def use_timestamped_reprod_if_exists(original_path):
122120
123121 return returned_path
124122
123+
125124def collect_eb_files (base_path ):
126125 """
127126 Scan for .eb files and their corresponding *-easybuild-devel files,
128127 extract the major EasyBuild version from devel files, and group .eb files by major version.
129128 For folders containing 'EasyBuild' or 'EESSI-extend', assume the loaded EasyBuild version if extraction fails.
130-
129+
131130 Parameters:
132131 base_path (str): Root folder to scan for .eb files.
133132
134133 Returns:
135134 dict: {major_version: [list of .eb file paths]}
136135 """
137136 eb_files_by_version = defaultdict (list )
138- version_pattern = re .compile (r' software/EasyBuild/(\d+)\.(\d+)\.(\d+)/bin' )
137+ version_pattern = re .compile (r" software/EasyBuild/(\d+)\.(\d+)\.(\d+)/bin" )
139138
140139 # Get major version from loaded EasyBuild installation for exceptions
141140 easybuild_major_version = str (EASYBUILD_VERSION .version [0 ])
142141
143142 # Find all .eb files recursively
144- eb_files = glob .glob (os .path .join (base_path , ' */*/easybuild/*.eb' ))
143+ eb_files = glob .glob (os .path .join (base_path , " */*/easybuild/*.eb" ))
145144
146145 for eb_file in eb_files :
147146 folder = os .path .dirname (eb_file )
148147
149148 # Look for the -easybuild-devel file in the same folder
150- devel_files = glob .glob (os .path .join (folder , ' *-easybuild-devel' ))
149+ devel_files = glob .glob (os .path .join (folder , " *-easybuild-devel" ))
151150 if not devel_files :
152151 raise FileNotFoundError (f"No *-easybuild-devel file found in folder: { folder } " )
153152
154153 # Pick the latest devel file if multiple exist
155154 latest_devel = max (devel_files , key = os .path .getmtime )
156155
157156 # Extract the EasyBuild version
158- with open (latest_devel , 'r' ) as f :
157+ with open (latest_devel , "r" ) as f :
159158 content = f .read ()
160159 match = version_pattern .search (content )
161160
162161 # Handle exception folders
163- if ' EasyBuild' in folder or ' EESSI-extend' in folder :
162+ if " EasyBuild" in folder or " EESSI-extend" in folder :
164163 major_version = match .group (1 ) if match else easybuild_major_version
165164 # Don't add EESSI-extend to EB4 or the same file will appear twice
166- if ' EESSI-extend' in folder and major_version == '4' :
165+ if " EESSI-extend" in folder and major_version == "4" :
167166 continue
168167 else :
169168 if not match :
@@ -183,7 +182,7 @@ def collect_eb_files(base_path):
183182 "-e" ,
184183 required = True ,
185184 choices = VALID_EESSI_VERSIONS ,
186- help = f"Allowed versions: { ', ' .join (VALID_EESSI_VERSIONS )} "
185+ help = f"Allowed versions: { ', ' .join (VALID_EESSI_VERSIONS )} " ,
187186 )
188187
189188 args = parser .parse_args ()
@@ -192,7 +191,9 @@ def collect_eb_files(base_path):
192191 print (f"Using EESSI version: { eessi_version } " )
193192
194193 # We use a single architecture path to gather information about the software versions
195- base_path = f'/cvmfs/software.eessi.io/versions/{ eessi_version } /software/linux/{ EESSI_REFERENCE_ARCHITECTURE } /software/'
194+ base_path = (
195+ f"/cvmfs/software.eessi.io/versions/{ eessi_version } /software/linux/{ EESSI_REFERENCE_ARCHITECTURE } /software/"
196+ )
196197 result = collect_eb_files (base_path )
197198
198199 set_up_configuration (args = "" )
@@ -203,56 +204,66 @@ def collect_eb_files(base_path):
203204 eessi_software ["eessi_version" ][eessi_version ] = {}
204205 # Add a timestamp
205206 eessi_software ["timestamp" ] = datetime .now (timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
206-
207+
207208 # Store the toolchain hierarchies supported by the EESSI version
208209 eessi_software ["eessi_version" ][eessi_version ]["toolchain_hierarchy" ] = {}
209210 for top_level_toolchain in EESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINS [eessi_version ]:
210211 toolchain_family = f"{ top_level_toolchain ['name' ]} _{ top_level_toolchain ['version' ]} "
211212 # Get the hierarchy and always add the system toolchain
212- eessi_software ["eessi_version" ][eessi_version ]["toolchain_hierarchy" ][toolchain_family ] = [{'name' : 'system' , 'version' : 'system' }] + get_toolchain_hierarchy (top_level_toolchain )
213-
213+ eessi_software ["eessi_version" ][eessi_version ]["toolchain_hierarchy" ][toolchain_family ] = [
214+ {"name" : "system" , "version" : "system" }
215+ ] + get_toolchain_hierarchy (top_level_toolchain )
216+
214217 for eb_version_of_install , files in sorted (result .items ()):
215218 print (f"Major version { eb_version_of_install } :" )
216219 if eb_version_of_install == str (EASYBUILD_VERSION .version [0 ]):
217220 total_files = len (files )
218221 for i , file in enumerate (files , start = 1 ):
219222 percent = (i / total_files ) * 100
220223 print (f"{ percent :.1f} % - { file } " )
221-
224+
222225 # Don't try to parse an EasyBuild easyconfig that is not the same major release
223- if ' /software/EasyBuild/' in file and f' /EasyBuild/{ eb_version_of_install } ' not in file :
226+ if " /software/EasyBuild/" in file and f" /EasyBuild/{ eb_version_of_install } " not in file :
224227 continue
225228 # print(process_easyconfig(path)[0]['ec'].asdict())
226-
229+
227230 eb_hooks_path = use_timestamped_reprod_if_exists (f"{ os .path .dirname (file )} /reprod/easyblocks" )
228- easyblocks_dir = include_easyblocks (tmpdir , [eb_hooks_path + "/*.py" ])
231+ easyblocks_dir = include_easyblocks (tmpdir , [eb_hooks_path + "/*.py" ])
229232 with suppress_stdout ():
230- parsed_ec = process_easyconfig (file )[0 ]
233+ parsed_ec = process_easyconfig (file )[0 ]
231234 # included easyblocks are the first entry in sys.path, so just pop them but keep a list of what was used
232235 sys .path .pop (0 )
233- easyblocks_used = [os .path .basename (f ) for f in glob .glob (f"{ easyblocks_dir } /**/*.py" , recursive = True ) if os .path .basename (f ) != '__init__.py' ]
236+ easyblocks_used = [
237+ os .path .basename (f )
238+ for f in glob .glob (f"{ easyblocks_dir } /**/*.py" , recursive = True )
239+ if os .path .basename (f ) != "__init__.py"
240+ ]
234241 shutil .rmtree (easyblocks_dir )
235-
242+
236243 # Use the path as the key since we know it is unique
237- eessi_software ["eessi_version" ][eessi_version ][file ] = parsed_ec ['ec' ].asdict ()
238- eessi_software ["eessi_version" ][eessi_version ][file ][' mtime' ] = os .path .getmtime (file )
239-
244+ eessi_software ["eessi_version" ][eessi_version ][file ] = parsed_ec ["ec" ].asdict ()
245+ eessi_software ["eessi_version" ][eessi_version ][file ][" mtime" ] = os .path .getmtime (file )
246+
240247 # Make sure we can load the module before adding it's information to the main dict
241248 try :
242- eessi_software ["eessi_version" ][eessi_version ][file ]['required_modules' ] = load_and_list_modules (parsed_ec ['full_mod_name' ])
249+ eessi_software ["eessi_version" ][eessi_version ][file ]["required_modules" ] = load_and_list_modules (
250+ parsed_ec ["full_mod_name" ]
251+ )
243252 except RuntimeError as e :
244253 print (f"Ignoring { file } due to error processing module: { e } " )
245254 eessi_software ["eessi_version" ][eessi_version ].pop (file )
246255 continue
247256
248257 # Store everything we now know about the installation as a dict
249258 # Add important data that is related to the module environment
250- eessi_software ["eessi_version" ][eessi_version ][file ]['full_mod_name' ] = parsed_ec ['full_mod_name' ]
251- eessi_software ["eessi_version" ][eessi_version ][file ]['short_mod_name' ] = parsed_ec ['short_mod_name' ]
252- eessi_software ["eessi_version" ][eessi_version ][file ]['required_modules' ] = load_and_list_modules (parsed_ec ['full_mod_name' ])
259+ eessi_software ["eessi_version" ][eessi_version ][file ]["full_mod_name" ] = parsed_ec ["full_mod_name" ]
260+ eessi_software ["eessi_version" ][eessi_version ][file ]["short_mod_name" ] = parsed_ec ["short_mod_name" ]
261+ eessi_software ["eessi_version" ][eessi_version ][file ]["required_modules" ] = load_and_list_modules (
262+ parsed_ec ["full_mod_name" ]
263+ )
253264 # Retain the easyblocks used so we can use a heuristic to figure out the type of extensions (R, Python, Perl)
254- eessi_software ["eessi_version" ][eessi_version ][file ][' easyblocks' ] = easyblocks_used
255-
265+ eessi_software ["eessi_version" ][eessi_version ][file ][" easyblocks" ] = easyblocks_used
266+
256267 # Store the result
257268 with open (f"eessi_software_{ eessi_version } -eb{ str (EASYBUILD_VERSION .version [0 ])} .yaml" , "w" ) as f :
258269 yaml .dump (eessi_software , f )
0 commit comments