From 67ec08095bfcc8ca9b1289ec2163318d090d7ccd Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 4 Feb 2025 18:02:34 +0100 Subject: [PATCH 1/3] Remove `LibSymlink.UNKNOWN` Automatically do the detection if not done yet to avoid forgetting it. --- easybuild/framework/easyblock.py | 23 +++++++++++++---------- test/framework/easyblock.py | 5 ++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index cb71615b8e..1eb610b2c8 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -134,7 +134,7 @@ class LibSymlink(Enum): - LIB64_TO_LIB: 'lib64' is a symlink to 'lib' - NEITHER: neither 'lib' is a symlink to 'lib64', nor 'lib64' is a symlink to 'lib' - """ - UNKNOWN, LIB_TO_LIB64, LIB64_TO_LIB, NEITHER = range(0, 4) + LIB_TO_LIB64, LIB64_TO_LIB, NEITHER = range(3) class EasyBlock(object): @@ -226,7 +226,7 @@ def __init__(self, ec, logfile=None): self.install_subdir = None # track status of symlink between library directories - self.install_lib_symlink = LibSymlink.UNKNOWN + self._install_lib_symlink = None # indicates whether build should be performed in installation dir self.build_in_installdir = self.cfg['buildininstalldir'] @@ -311,6 +311,13 @@ def __init__(self, ec, logfile=None): self.log.info("Init completed for application name %s version %s" % (self.name, self.version)) + @property + def install_lib_symlink(self): + """Return symlink state of lib/lib64 folders""" + if self._install_lib_symlink is None: + self.check_install_lib_symlink() + return self._install_lib_symlink + def post_init(self): """ Run post-initialization tasks. @@ -1719,10 +1726,6 @@ def expand_module_search_path(self, search_path, path_type=ModEnvVarType.PATH_WI abs_glob = os.path.join(self.installdir, search_path) exp_search_paths = [abs_glob] if search_path == "" else glob.glob(abs_glob) - # Explicitly check symlink state between lib dirs if it is still undefined (e.g. --module-only) - if self.install_lib_symlink == LibSymlink.UNKNOWN: - self.check_install_lib_symlink() - retained_search_paths = [] for abs_path in exp_search_paths: # return relative paths @@ -1751,16 +1754,16 @@ def expand_module_search_path(self, search_path, path_type=ModEnvVarType.PATH_WI return retained_search_paths def check_install_lib_symlink(self): - """Check symlink state between library directories in installation prefix""" + """Update the symlink state between library directories in installation prefix""" lib_dir = os.path.join(self.installdir, 'lib') lib64_dir = os.path.join(self.installdir, 'lib64') - self.install_lib_symlink = LibSymlink.NEITHER + self._install_lib_symlink = LibSymlink.NEITHER if os.path.exists(lib_dir) and os.path.exists(lib64_dir): if os.path.islink(lib_dir) and os.path.samefile(lib_dir, lib64_dir): - self.install_lib_symlink = LibSymlink.LIB_TO_LIB64 + self._install_lib_symlink = LibSymlink.LIB_TO_LIB64 elif os.path.islink(lib64_dir) and os.path.samefile(lib_dir, lib64_dir): - self.install_lib_symlink = LibSymlink.LIB64_TO_LIB + self._install_lib_symlink = LibSymlink.LIB64_TO_LIB def make_module_req_guess(self): """ diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index d54435b46b..59c061d78e 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -439,7 +439,7 @@ def test_make_module_req(self): for path in ('bin', ('bin', 'testdir'), 'sbin', 'share', ('share', 'man'), 'lib', 'lib64'): path_components = (path, ) if isinstance(path, str) else path os.mkdir(os.path.join(eb.installdir, *path_components)) - eb.install_lib_symlink = LibSymlink.NEITHER + eb.check_install_lib_symlink() write_file(os.path.join(eb.installdir, 'foo.jar'), 'foo.jar') write_file(os.path.join(eb.installdir, 'bla.jar'), 'bla.jar') @@ -505,7 +505,7 @@ def test_make_module_req(self): write_file(os.path.join(eb.installdir, 'lib', 'libfoo.so'), 'test') shutil.rmtree(os.path.join(eb.installdir, 'lib64')) os.symlink('lib', os.path.join(eb.installdir, 'lib64')) - eb.install_lib_symlink = LibSymlink.LIB64_TO_LIB + eb.check_install_lib_symlink() with eb.module_generator.start_module_creation(): guess = eb.make_module_req() if get_module_syntax() == 'Tcl': @@ -3215,7 +3215,6 @@ def test_expand_module_search_path(self): write_file(os.path.join(eb.installdir, 'dir_full_subdirs', 'subdir1', 'file12.txt'), 'test file 1.2') write_file(os.path.join(eb.installdir, 'dir_full_subdirs', 'subdir2', 'file21.txt'), 'test file 2.1') - self.assertEqual(eb.install_lib_symlink, LibSymlink.UNKNOWN) eb.check_install_lib_symlink() self.assertEqual(eb.install_lib_symlink, LibSymlink.NEITHER) From 2aa708500eba583020c91e80d4f5df51895ce456 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 4 Feb 2025 18:03:22 +0100 Subject: [PATCH 2/3] Use default argument --- easybuild/tools/modules.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/easybuild/tools/modules.py b/easybuild/tools/modules.py index 5986a2dcf3..b5b2d05e37 100644 --- a/easybuild/tools/modules.py +++ b/easybuild/tools/modules.py @@ -152,18 +152,15 @@ class ModuleEnvironmentVariable: Contents of environment variable is a list of unique strings """ - def __init__(self, contents, var_type=None, delim=os.pathsep): + def __init__(self, contents, var_type=ModEnvVarType.PATH_WITH_FILES, delim=os.pathsep): """ Initialize new environment variable Actual contents of the environment variable are held in self.contents - By default, environment variable is a (list of) paths with files in them + By default, the environment variable is a list of paths with files in them Existence of paths and their contents are not checked at init """ self.contents = contents self.delim = delim - - if var_type is None: - var_type = ModEnvVarType.PATH_WITH_FILES self.type = var_type self.log = fancylogger.getLogger(self.__class__.__name__, fname=False) @@ -283,7 +280,7 @@ def __setattr__(self, name, value): # special variables that require files in their top directories if name in ('LD_LIBRARY_PATH', 'PATH'): - kwargs.update({'var_type': ModEnvVarType.PATH_WITH_TOP_FILES}) + kwargs['var_type'] = ModEnvVarType.PATH_WITH_TOP_FILES return super().__setattr__(name, ModuleEnvironmentVariable(contents, **kwargs)) From a2c4c8c431a2256115e225644fc0481fb599dfcc Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 4 Feb 2025 18:03:49 +0100 Subject: [PATCH 3/3] Faster iteration in ModuleLoadEnvironment --- easybuild/tools/modules.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/easybuild/tools/modules.py b/easybuild/tools/modules.py index b5b2d05e37..8ebbb88eb3 100644 --- a/easybuild/tools/modules.py +++ b/easybuild/tools/modules.py @@ -294,8 +294,7 @@ def items(self): - key = attribute name - value = its "contents" attribute """ - for attr in self.__dict__: - yield attr, getattr(self, attr) + return self.__dict__.items() def update(self, new_env): """Update contents of environment from given dictionary""" @@ -318,10 +317,7 @@ def environ(self): Return dict with mapping of ModuleEnvironmentVariables names with their contents Equivalent in shape to os.environ """ - mapping = {} - for envar_name, envar_contents in self.items(): - mapping.update({envar_name: str(envar_contents)}) - return mapping + return {envar_name: str(envar_contents) for envar_name, envar_contents in self.items()} class ModulesTool(object):