5757from easybuild .tools .utilities import get_subclasses , nub
5858
5959
60+ MODULE_LOAD_ENV_HEADERS = 'HEADERS'
61+
6062# software root/version environment variable name prefixes
6163ROOT_ENV_VAR_NAME_PREFIX = "EBROOT"
6264VERSION_ENV_VAR_NAME_PREFIX = "EBVERSION"
@@ -241,18 +243,38 @@ def is_path(self):
241243
242244
243245class ModuleLoadEnvironment :
244- """Changes to environment variables that should be made when environment module is loaded"""
246+ """
247+ Changes to environment variables that should be made when environment module is loaded.
248+ - Environment variables are defined as ModuleEnvironmentVariables instances
249+ with attribute name equal to environment variable name.
250+ - Aliases are arbitrary names that serve to apply changes to lists of
251+ environment variables
252+ - Only environment variables attributes are public. Other attributes like
253+ aliases are private.
254+ """
245255
246- def __init__ (self ):
256+ def __init__ (self , aliases = None ):
247257 """
248258 Initialize default environment definition
249259 Paths are relative to root of installation directory
260+
261+ :aliases: dict defining environment variables aliases
250262 """
263+ self ._aliases = {}
264+ if aliases is not None :
265+ try :
266+ for alias_name , alias_vars in aliases .items ():
267+ self .update_alias (alias_name , alias_vars )
268+ except AttributeError as err :
269+ raise EasyBuildError (
270+ "Wrong format for aliases defitions passed to ModuleLoadEnvironment. "
271+ f"Expected a dictionary but got: { type (aliases )} ."
272+ ) from err
273+
251274 self .ACLOCAL_PATH = [os .path .join ('share' , 'aclocal' )]
252275 self .CLASSPATH = ['*.jar' ]
253276 self .CMAKE_LIBRARY_PATH = ['lib64' ] # only needed for installations with standalone lib64
254277 self .CMAKE_PREFIX_PATH = ['' ]
255- self .CPATH = SEARCH_PATH_HEADER_DIRS
256278 self .GI_TYPELIB_PATH = [os .path .join (x , 'girepository-*' ) for x in SEARCH_PATH_LIB_DIRS ]
257279 self .LD_LIBRARY_PATH = SEARCH_PATH_LIB_DIRS
258280 self .LIBRARY_PATH = SEARCH_PATH_LIB_DIRS
@@ -261,11 +283,29 @@ def __init__(self):
261283 self .PKG_CONFIG_PATH = [os .path .join (x , 'pkgconfig' ) for x in SEARCH_PATH_LIB_DIRS + ['share' ]]
262284 self .XDG_DATA_DIRS = ['share' ]
263285
286+ # environment variables with known aliases
287+ # e.g. search paths to C/C++ headers
288+ for envar_name in self ._aliases .get (MODULE_LOAD_ENV_HEADERS , []):
289+ setattr (self , envar_name , SEARCH_PATH_HEADER_DIRS )
290+
264291 def __setattr__ (self , name , value ):
265292 """
266293 Specific restrictions for ModuleLoadEnvironment attributes:
294+ - public attributes are instances of ModuleEnvironmentVariable with uppercase names
295+ - private attributes are allowed with any name
296+ """
297+ if name .startswith ('_' ):
298+ # do not control protected/private attributes
299+ return super ().__setattr__ (name , value )
300+
301+ return self .__set_module_environment_variable (name , value )
302+
303+ def __set_module_environment_variable (self , name , value ):
304+ """
305+ Specific restrictions for ModuleEnvironmentVariable attributes:
267306 - attribute names are uppercase
268- - attributes are instances of ModuleEnvironmentVariable
307+ - dictionaries are unpacked into arguments of ModuleEnvironmentVariable
308+ - controls variables with special types (e.g. PATH, LD_LIBRARY_PATH)
269309 """
270310 if name != name .upper ():
271311 raise EasyBuildError (f"Names of ModuleLoadEnvironment attributes must be uppercase, got '{ name } '" )
@@ -284,17 +324,24 @@ def __setattr__(self, name, value):
284324
285325 return super ().__setattr__ (name , ModuleEnvironmentVariable (contents , ** kwargs ))
286326
327+ @property
328+ def vars (self ):
329+ """Return list of public ModuleEnvironmentVariable"""
330+
331+ return [envar for envar in self .__dict__ if not str (envar ).startswith ('_' )]
332+
287333 def __iter__ (self ):
288334 """Make the class iterable"""
289- yield from self .__dict__
335+ yield from self .vars
290336
291337 def items (self ):
292338 """
293339 Return key-value pairs for each attribute that is a ModuleEnvironmentVariable
294340 - key = attribute name
295341 - value = its "contents" attribute
296342 """
297- return self .__dict__ .items ()
343+ for attr in self .vars :
344+ yield attr , getattr (self , attr )
298345
299346 def update (self , new_env ):
300347 """Update contents of environment from given dictionary"""
@@ -304,6 +351,14 @@ def update(self, new_env):
304351 except AttributeError as err :
305352 raise EasyBuildError ("Cannot update ModuleLoadEnvironment from a non-dict variable" ) from err
306353
354+ def remove (self , var_name ):
355+ """
356+ Remove ModuleEnvironmentVariable attribute from instance
357+ Silently goes through if attribute is already missing
358+ """
359+ if var_name in self .vars :
360+ delattr (self , var_name )
361+
307362 @property
308363 def as_dict (self ):
309364 """
@@ -319,6 +374,48 @@ def environ(self):
319374 """
320375 return {envar_name : str (envar_contents ) for envar_name , envar_contents in self .items ()}
321376
377+ def alias (self , alias ):
378+ """
379+ Return iterator to search path variables for given alias
380+ """
381+ try :
382+ yield from [getattr (self , envar ) for envar in self ._aliases [alias ]]
383+ except KeyError as err :
384+ raise EasyBuildError (f"Unknown search path alias: { alias } " ) from err
385+ except AttributeError as err :
386+ raise EasyBuildError (f"Missing environment variable in '{ alias } alias" ) from err
387+
388+ def alias_vars (self , alias ):
389+ """
390+ Return list of environment variable names aliased by given alias
391+ """
392+ try :
393+ return self ._aliases [alias ]
394+ except KeyError as err :
395+ raise EasyBuildError (f"Unknown search path alias: { alias } " ) from err
396+
397+ def update_alias (self , alias , value ):
398+ """
399+ Update existing or non-existing alias with given search paths variables
400+ """
401+ if isinstance (value , str ):
402+ value = [value ]
403+
404+ try :
405+ self ._aliases [alias ] = [str (envar ) for envar in value ]
406+ except TypeError as err :
407+ raise TypeError ("ModuleLoadEnvironment aliases must be a list of strings" ) from err
408+
409+ def set_alias_vars (self , alias , value ):
410+ """
411+ Set value of search paths variables for given alias
412+ """
413+ try :
414+ for envar_name in self ._aliases [alias ]:
415+ setattr (self , envar_name , value )
416+ except KeyError as err :
417+ raise EasyBuildError (f"Unknown search path alias: { alias } " ) from err
418+
322419
323420class ModulesTool (object ):
324421 """An abstract interface to a tool that deals with modules."""
0 commit comments