diff --git a/easybuild/tools/modules.py b/easybuild/tools/modules.py index aa2dc7b7e4..ef357b69d1 100644 --- a/easybuild/tools/modules.py +++ b/easybuild/tools/modules.py @@ -1697,7 +1697,7 @@ def get_software_root(name, with_env_var=False): return res -def get_software_libdir(name, only_one=True, fs=None): +def get_software_libdir(name, only_one=True, fs=None, full_path=False): """ Find library subdirectories for the specified software package. @@ -1708,51 +1708,57 @@ def get_software_libdir(name, only_one=True, fs=None): :param name: name of the software package :param only_one: indicates whether only one lib path is expected to be found :param fs: only retain library subdirs that contain one of the files in this list + :param full_path: Include the software root in the returned path, or just return the subfolder found """ lib_subdirs = ['lib', 'lib64'] root = get_software_root(name) - res = [] - if root: - for lib_subdir in lib_subdirs: - lib_dir_path = os.path.join(root, lib_subdir) - if os.path.exists(lib_dir_path): - # take into account that lib64 could be a symlink to lib (or vice versa) - # see https://github.com/easybuilders/easybuild-framework/issues/3139 - if any(os.path.samefile(lib_dir_path, os.path.join(root, x)) for x in res): - _log.debug("%s is the same as one of the other paths, so skipping it", lib_dir_path) - - elif fs is None or any(os.path.exists(os.path.join(lib_dir_path, f)) for f in fs): - _log.debug("Retaining library subdir '%s' (found at %s)", lib_subdir, lib_dir_path) - res.append(lib_subdir) - - elif build_option('extended_dry_run'): - res.append(lib_subdir) - break - - # if no library subdir was found, return None - if not res: - return None - if only_one: - if len(res) == 1: - res = res[0] - else: - if fs is None and len(res) == 2: - # if both lib and lib64 were found, check if only one (exactly) has libraries; - # this is needed for software with library archives in lib64 but other files/directories in lib - lib_glob = ['*.%s' % ext for ext in ['a', get_shared_lib_ext()]] - has_libs = [any(glob.glob(os.path.join(root, subdir, f)) for f in lib_glob) for subdir in res] - if has_libs[0] and not has_libs[1]: - return res[0] - elif has_libs[1] and not has_libs[0]: - return res[1] - - raise EasyBuildError("Multiple library subdirectories found for %s in %s: %s", - name, root, ', '.join(res)) - return res - else: + if not root: # return None if software package root could not be determined return None + found_subdirs = [] + for lib_subdir in lib_subdirs: + lib_dir_path = os.path.join(root, lib_subdir) + if os.path.exists(lib_dir_path): + # take into account that lib64 could be a symlink to lib (or vice versa) + # see https://github.com/easybuilders/easybuild-framework/issues/3139 + if any(os.path.samefile(lib_dir_path, os.path.join(root, x)) for x in found_subdirs): + _log.debug("%s is the same as one of the other paths, so skipping it", lib_dir_path) + + elif fs is None or any(os.path.exists(os.path.join(lib_dir_path, f)) for f in fs): + _log.debug("Retaining library subdir '%s' (found at %s)", lib_subdir, lib_dir_path) + found_subdirs.append(lib_subdir) + + elif build_option('extended_dry_run'): + found_subdirs.append(lib_subdir) + break + + # if no library subdir was found, return None + if not found_subdirs: + return None + if full_path: + res = [os.path.join(root, subdir) for subdir in found_subdirs] + else: + res = found_subdirs + if only_one: + if len(res) == 1: + res = res[0] + else: + if fs is None and len(res) == 2: + # if both lib and lib64 were found, check if only one (exactly) has libraries; + # this is needed for software with library archives in lib64 but other files/directories in lib + lib_glob = ['*.%s' % ext for ext in ['a', get_shared_lib_ext()]] + has_libs = [any(glob.glob(os.path.join(root, subdir, f)) for f in lib_glob) + for subdir in found_subdirs] + if has_libs[0] and not has_libs[1]: + return res[0] + if has_libs[1] and not has_libs[0]: + return res[1] + + raise EasyBuildError("Multiple library subdirectories found for %s in %s: %s", + name, root, ', '.join(found_subdirs)) + return res + def get_software_version_env_var_name(name): """Return name of environment variable for software root.""" diff --git a/test/framework/modules.py b/test/framework/modules.py index 5cd783694d..bdd1e595a0 100644 --- a/test/framework/modules.py +++ b/test/framework/modules.py @@ -686,6 +686,7 @@ def test_get_software_root_version_libdir(self): self.assertEqual(get_software_root(name), root) self.assertEqual(get_software_version(name), version) self.assertEqual(get_software_libdir(name), 'lib') + self.assertEqual(get_software_libdir(name, full_path=True), os.path.join(root, 'lib')) os.environ.pop('EBROOT%s' % env_var_name) os.environ.pop('EBVERSION%s' % env_var_name) @@ -694,30 +695,39 @@ def test_get_software_root_version_libdir(self): root = os.path.join(tmpdir, name) mkdir(os.path.join(root, 'lib64')) os.environ['EBROOT%s' % env_var_name] = root + + def check_get_software_libdir(expected, **additional_args): + self.assertEqual(get_software_libdir(name, **additional_args), expected) + if isinstance(expected, list): + expected = [os.path.join(root, d) for d in expected] + elif expected: + expected = os.path.join(root, expected) + self.assertEqual(get_software_libdir(name, full_path=True, **additional_args), expected) + write_file(os.path.join(root, 'lib', 'libfoo.a'), 'foo') - self.assertEqual(get_software_libdir(name), 'lib') + check_get_software_libdir('lib') remove_file(os.path.join(root, 'lib', 'libfoo.a')) # also check vice versa with *shared* library in lib64 shlib_ext = get_shared_lib_ext() write_file(os.path.join(root, 'lib64', 'libfoo.' + shlib_ext), 'foo') - self.assertEqual(get_software_libdir(name), 'lib64') + check_get_software_libdir('lib64') remove_file(os.path.join(root, 'lib64', 'libfoo.' + shlib_ext)) # check expected result of get_software_libdir with multiple lib subdirs self.assertErrorRegex(EasyBuildError, "Multiple library subdirectories found.*", get_software_libdir, name) - self.assertEqual(get_software_libdir(name, only_one=False), ['lib', 'lib64']) + check_get_software_libdir(only_one=False, expected=['lib', 'lib64']) # only directories containing files in specified list should be retained write_file(os.path.join(root, 'lib64', 'foo'), 'foo') - self.assertEqual(get_software_libdir(name, fs=['foo']), 'lib64') + check_get_software_libdir(fs=['foo'], expected='lib64') # duplicate paths due to symlink get filtered remove_dir(os.path.join(root, 'lib64')) symlink(os.path.join(root, 'lib'), os.path.join(root, 'lib64')) - self.assertEqual(get_software_libdir(name), 'lib') + check_get_software_libdir('lib') # same goes for lib symlinked to lib64 remove_file(os.path.join(root, 'lib64')) @@ -725,19 +735,20 @@ def test_get_software_root_version_libdir(self): mkdir(os.path.join(root, 'lib64')) symlink(os.path.join(root, 'lib64'), os.path.join(root, 'lib')) # still returns 'lib' because that's the first subdir considered - self.assertEqual(get_software_libdir(name), 'lib') + check_get_software_libdir('lib') # clean up for previous tests os.environ.pop('EBROOT%s' % env_var_name) # if root/version for specified software package can not be found, these functions should return None - self.assertEqual(get_software_root('foo'), None) - self.assertEqual(get_software_version('foo'), None) - self.assertEqual(get_software_libdir('foo'), None) + self.assertEqual(get_software_root(name), None) + self.assertEqual(get_software_version(name), None) + check_get_software_libdir(None) # if no library subdir is found, get_software_libdir should return None os.environ['EBROOTFOO'] = tmpdir self.assertEqual(get_software_libdir('foo'), None) + self.assertEqual(get_software_libdir('foo', full_path=True), None) os.environ.pop('EBROOTFOO') shutil.rmtree(tmpdir)