Skip to content

Commit 0aea9a4

Browse files
committed
Avoid duplicate paths in make_module_req
Expand all globs and iterate over combined list of paths per key This allows elimination of duplicate paths and greatly simplified code.
1 parent 17af497 commit 0aea9a4

File tree

2 files changed

+47
-37
lines changed

2 files changed

+47
-37
lines changed

easybuild/framework/easyblock.py

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,58 +1291,57 @@ def make_module_req(self):
12911291
note = "note: glob patterns are not expanded and existence checks "
12921292
note += "for paths are skipped for the statements below due to dry run"
12931293
lines.append(self.module_generator.comment(note))
1294-
else:
1295-
lib64_is_symlink = (all(os.path.isdir(path) for path in ['lib', 'lib64'])
1296-
and os.path.samefile('lib', 'lib64'))
12971294

12981295
# for these environment variables, the corresponding subdirectory must include at least one file
1299-
keys_requiring_files = ('CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'PATH', 'CMAKE_LIBRARY_PATH')
1296+
keys_requiring_files = {'CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'PATH', 'CMAKE_LIBRARY_PATH'}
13001297

13011298
for key, reqs in sorted(requirements.items()):
13021299
if isinstance(reqs, string_type):
13031300
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
13041301
reqs = [reqs]
13051302
if self.dry_run:
13061303
self.dry_run_msg(" $%s: %s" % (key, ', '.join(reqs)))
1307-
1308-
for path in reqs:
1309-
# only use glob if the string is non-empty
1310-
if path and not self.dry_run:
1311-
paths = glob.glob(path)
1312-
# If lib64 is just a symlink to lib we fixup the paths to avoid duplicates
1313-
if lib64_is_symlink:
1314-
fixed_paths = []
1315-
for path in paths:
1316-
if (path + os.path.sep).startswith('lib64' + os.path.sep):
1317-
# We only need CMAKE_LIBRARY_PATH if there is a separate lib64 path
1318-
if key == 'CMAKE_LIBRARY_PATH':
1319-
continue
1320-
path = path.replace('lib64', 'lib', 1)
1321-
fixed_paths.append(path)
1322-
if fixed_paths != paths:
1323-
self.log.info("Fixed symlink lib64 in paths for %s: %s -> %s",
1324-
key, paths, fixed_paths)
1325-
paths = fixed_paths
1326-
# Use a set to remove duplicates
1327-
paths = sorted(set(paths))
1328-
if paths and key in keys_requiring_files:
1329-
# only retain paths that contain at least one file
1330-
retained_paths = [
1331-
path for path in paths
1332-
if os.path.isdir(os.path.join(self.installdir, path))
1333-
and dir_contains_files(os.path.join(self.installdir, path))
1334-
]
1304+
# Don't expand globs or do any filtering below for dry run
1305+
paths = sorted(reqs)
1306+
else:
1307+
# Expand globs but only if the string is non-empty
1308+
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
1309+
paths = sorted(sum((glob.glob(path) if path else [path] for path in reqs), [])) # sum flattens to list
1310+
1311+
# If lib64 is just a symlink to lib we fixup the paths to avoid duplicates
1312+
lib64_is_symlink = (all(os.path.isdir(path) for path in ['lib', 'lib64'])
1313+
and os.path.samefile('lib', 'lib64'))
1314+
if lib64_is_symlink:
1315+
fixed_paths = []
1316+
for path in paths:
1317+
if (path + os.path.sep).startswith('lib64' + os.path.sep):
1318+
# We only need CMAKE_LIBRARY_PATH if there is a separate lib64 path, so skip symlink
1319+
if key == 'CMAKE_LIBRARY_PATH':
1320+
continue
1321+
path = path.replace('lib64', 'lib', 1)
1322+
fixed_paths.append(path)
1323+
if fixed_paths != paths:
1324+
self.log.info("Fixed symlink lib64 in paths for %s: %s -> %s", key, paths, fixed_paths)
1325+
paths = fixed_paths
1326+
# Use a set to remove duplicates, e.g. by having lib64 and lib which get fixed to lib and lib above
1327+
paths = sorted(set(paths))
1328+
if key in keys_requiring_files:
1329+
# only retain paths that contain at least one file
1330+
retained_paths = [
1331+
path for path in paths
1332+
if os.path.isdir(os.path.join(self.installdir, path))
1333+
and dir_contains_files(os.path.join(self.installdir, path))
1334+
]
1335+
if retained_paths != paths:
13351336
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
1336-
key, paths, retained_paths)
1337+
key, paths, retained_paths)
13371338
paths = retained_paths
1338-
else:
1339-
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
1340-
paths = [path]
13411339

1340+
if paths:
13421341
lines.append(self.module_generator.prepend_paths(key, paths))
13431342
if self.dry_run:
13441343
self.dry_run_msg('')
1345-
1344+
13461345
if old_dir is not None:
13471346
change_dir(old_dir)
13481347

test/framework/easyblock.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,17 @@ def test_make_module_req(self):
386386
elif get_module_syntax() == 'Lua':
387387
self.assertFalse('prepend_path("CMAKE_LIBRARY_PATH", pathJoin(root, "lib64"))' in guess)
388388

389+
# With files in /lib and /lib64 symlinked to /lib there should be exactly 1 entry for (LD_)LIBRARY_PATH
390+
# pointing to /lib
391+
for var in ('LIBRARY_PATH', 'LD_LIBRARY_PATH'):
392+
if get_module_syntax() == 'Tcl':
393+
self.assertFalse(re.search(r"^prepend-path\s+%s\s+\$root/lib64$" % var, guess, re.M))
394+
self.assertEqual(len(re.findall(r"^prepend-path\s+%s\s+\$root/lib$" % var, guess, re.M)), 1)
395+
elif get_module_syntax() == 'Lua':
396+
self.assertFalse(re.search(r'^prepend_path\("%s", pathJoin\(root, "lib64"\)\)$' % var, guess, re.M))
397+
self.assertEqual(len(re.findall(r'^prepend_path\("%s", pathJoin\(root, "lib"\)\)$' % var,
398+
guess, re.M)), 1)
399+
389400
# check for behavior when a string value is used as dict value by make_module_req_guesses
390401
eb.make_module_req_guess = lambda: {'PATH': 'bin'}
391402
txt = eb.make_module_req()

0 commit comments

Comments
 (0)