Skip to content

Commit 090f8c1

Browse files
authored
Merge pull request #3176 from Flamefire/improve_make_module_req
Improve make_module_req
2 parents 5979165 + 8dfbd25 commit 090f8c1

File tree

3 files changed

+106
-44
lines changed

3 files changed

+106
-44
lines changed

easybuild/framework/easyblock.py

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,47 +1282,69 @@ def make_module_req(self):
12821282

12831283
lines = ['\n']
12841284
if os.path.isdir(self.installdir):
1285-
change_dir(self.installdir)
1285+
old_dir = change_dir(self.installdir)
1286+
else:
1287+
old_dir = None
12861288

1289+
if self.dry_run:
1290+
self.dry_run_msg("List of paths that would be searched and added to module file:\n")
1291+
note = "note: glob patterns are not expanded and existence checks "
1292+
note += "for paths are skipped for the statements below due to dry run"
1293+
lines.append(self.module_generator.comment(note))
1294+
1295+
# for these environment variables, the corresponding subdirectory must include at least one file
1296+
keys_requiring_files = set(('PATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'CPATH',
1297+
'CMAKE_PREFIX_PATH', 'CMAKE_LIBRARY_PATH'))
1298+
1299+
for key, reqs in sorted(requirements.items()):
1300+
if isinstance(reqs, string_type):
1301+
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
1302+
reqs = [reqs]
12871303
if self.dry_run:
1288-
self.dry_run_msg("List of paths that would be searched and added to module file:\n")
1289-
note = "note: glob patterns are not expanded and existence checks "
1290-
note += "for paths are skipped for the statements below due to dry run"
1291-
lines.append(self.module_generator.comment(note))
1292-
1293-
# for these environment variables, the corresponding subdirectory must include at least one file
1294-
keys_requiring_files = ('CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'PATH')
1295-
1296-
for key in sorted(requirements):
1297-
if self.dry_run:
1298-
self.dry_run_msg(" $%s: %s" % (key, ', '.join(requirements[key])))
1299-
reqs = requirements[key]
1300-
if isinstance(reqs, string_type):
1301-
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
1302-
reqs = [reqs]
1303-
1304-
for path in reqs:
1305-
# only use glob if the string is non-empty
1306-
if path and not self.dry_run:
1307-
paths = sorted(glob.glob(path))
1308-
if paths and key in keys_requiring_files:
1309-
# only retain paths that contain at least one file
1310-
retained_paths = [
1311-
path for path in paths
1312-
if os.path.isdir(os.path.join(self.installdir, path))
1313-
and dir_contains_files(os.path.join(self.installdir, path))
1314-
]
1315-
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
1316-
key, paths, retained_paths)
1317-
paths = retained_paths
1318-
else:
1319-
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
1320-
paths = [path]
1304+
self.dry_run_msg(" $%s: %s" % (key, ', '.join(reqs)))
1305+
# Don't expand globs or do any filtering below for dry run
1306+
paths = sorted(reqs)
1307+
else:
1308+
# Expand globs but only if the string is non-empty
1309+
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
1310+
paths = sorted(sum((glob.glob(path) if path else [path] for path in reqs), [])) # sum flattens to list
1311+
1312+
# If lib64 is just a symlink to lib we fixup the paths to avoid duplicates
1313+
lib64_is_symlink = (all(os.path.isdir(path) for path in ['lib', 'lib64'])
1314+
and os.path.samefile('lib', 'lib64'))
1315+
if lib64_is_symlink:
1316+
fixed_paths = []
1317+
for path in paths:
1318+
if (path + os.path.sep).startswith('lib64' + os.path.sep):
1319+
# We only need CMAKE_LIBRARY_PATH if there is a separate lib64 path, so skip symlink
1320+
if key == 'CMAKE_LIBRARY_PATH':
1321+
continue
1322+
path = path.replace('lib64', 'lib', 1)
1323+
fixed_paths.append(path)
1324+
if fixed_paths != paths:
1325+
self.log.info("Fixed symlink lib64 in paths for %s: %s -> %s", key, paths, fixed_paths)
1326+
paths = fixed_paths
1327+
# Use a set to remove duplicates, e.g. by having lib64 and lib which get fixed to lib and lib above
1328+
paths = sorted(set(paths))
1329+
if key in keys_requiring_files:
1330+
# only retain paths that contain at least one file
1331+
retained_paths = [
1332+
path for path in paths
1333+
if os.path.isdir(os.path.join(self.installdir, path))
1334+
and dir_contains_files(os.path.join(self.installdir, path))
1335+
]
1336+
if retained_paths != paths:
1337+
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
1338+
key, paths, retained_paths)
1339+
paths = retained_paths
1340+
1341+
if paths:
1342+
lines.append(self.module_generator.prepend_paths(key, paths))
1343+
if self.dry_run:
1344+
self.dry_run_msg('')
13211345

1322-
lines.append(self.module_generator.prepend_paths(key, paths))
1323-
if self.dry_run:
1324-
self.dry_run_msg('')
1325-
change_dir(self.orig_workdir)
1346+
if old_dir is not None:
1347+
change_dir(old_dir)
13261348

13271349
return ''.join(lines)
13281350

@@ -1342,6 +1364,8 @@ def make_module_req_guess(self):
13421364
'CLASSPATH': ['*.jar'],
13431365
'XDG_DATA_DIRS': ['share'],
13441366
'GI_TYPELIB_PATH': [os.path.join(x, 'girepository-*') for x in lib_paths],
1367+
'CMAKE_PREFIX_PATH': [''],
1368+
'CMAKE_LIBRARY_PATH': ['lib64'], # lib and lib32 are searched through the above
13451369
}
13461370

13471371
def load_module(self, mod_paths=None, purge=True, extra_modules=None):

test/framework/easyblock.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
from easybuild.tools.modules import reset_module_caches
5252
from easybuild.tools.utilities import time2str
5353
from easybuild.tools.version import get_git_revision, this_is_easybuild
54-
54+
from easybuild.tools.py2vs3 import string_type
5555

5656
class EasyBlockTest(EnhancedTestCase):
5757
""" Baseclass for easyblock testcases """
@@ -318,11 +318,10 @@ def test_make_module_req(self):
318318
os.makedirs(eb.installdir)
319319
open(os.path.join(eb.installdir, 'foo.jar'), 'w').write('foo.jar')
320320
open(os.path.join(eb.installdir, 'bla.jar'), 'w').write('bla.jar')
321-
os.mkdir(os.path.join(eb.installdir, 'bin'))
322-
os.mkdir(os.path.join(eb.installdir, 'bin', 'testdir'))
323-
os.mkdir(os.path.join(eb.installdir, 'sbin'))
324-
os.mkdir(os.path.join(eb.installdir, 'share'))
325-
os.mkdir(os.path.join(eb.installdir, 'share', 'man'))
321+
for path in ('bin', ('bin', 'testdir'), 'sbin', 'share', ('share', 'man'), 'lib', 'lib64'):
322+
if isinstance(path, string_type):
323+
path = (path, )
324+
os.mkdir(os.path.join(eb.installdir, *path))
326325
# this is not a path that should be picked up
327326
os.mkdir(os.path.join(eb.installdir, 'CPATH'))
328327

@@ -332,6 +331,7 @@ def test_make_module_req(self):
332331
self.assertTrue(re.search(r"^prepend-path\s+CLASSPATH\s+\$root/bla.jar$", guess, re.M))
333332
self.assertTrue(re.search(r"^prepend-path\s+CLASSPATH\s+\$root/foo.jar$", guess, re.M))
334333
self.assertTrue(re.search(r"^prepend-path\s+MANPATH\s+\$root/share/man$", guess, re.M))
334+
self.assertTrue(re.search(r"^prepend-path\s+CMAKE_PREFIX_PATH\s+\$root$", guess, re.M))
335335
# bin/ is not added to $PATH if it doesn't include files
336336
self.assertFalse(re.search(r"^prepend-path\s+PATH\s+\$root/bin$", guess, re.M))
337337
self.assertFalse(re.search(r"^prepend-path\s+PATH\s+\$root/sbin$", guess, re.M))
@@ -341,6 +341,7 @@ def test_make_module_req(self):
341341
self.assertTrue(re.search(r'^prepend_path\("CLASSPATH", pathJoin\(root, "bla.jar"\)\)$', guess, re.M))
342342
self.assertTrue(re.search(r'^prepend_path\("CLASSPATH", pathJoin\(root, "foo.jar"\)\)$', guess, re.M))
343343
self.assertTrue(re.search(r'^prepend_path\("MANPATH", pathJoin\(root, "share/man"\)\)$', guess, re.M))
344+
self.assertTrue('prepend_path("CMAKE_PREFIX_PATH", root)' in guess)
344345
# bin/ is not added to $PATH if it doesn't include files
345346
self.assertFalse(re.search(r'^prepend_path\("PATH", pathJoin\(root, "bin"\)\)$', guess, re.M))
346347
self.assertFalse(re.search(r'^prepend_path\("PATH", pathJoin\(root, "sbin"\)\)$', guess, re.M))
@@ -361,6 +362,41 @@ def test_make_module_req(self):
361362
else:
362363
self.assertTrue(False, "Unknown module syntax: %s" % get_module_syntax())
363364

365+
# Check that lib64 is only added to CMAKE_LIBRARY_PATH if there are files in there
366+
# but only if it is not a symlink to lib
367+
# -- No Files
368+
if get_module_syntax() == 'Tcl':
369+
self.assertFalse(re.search(r"^prepend-path\s+CMAKE_LIBRARY_PATH\s+\$root/lib64$", guess, re.M))
370+
elif get_module_syntax() == 'Lua':
371+
self.assertFalse('prepend_path("CMAKE_LIBRARY_PATH", pathJoin(root, "lib64"))' in guess)
372+
# -- With files
373+
open(os.path.join(eb.installdir, 'lib64', 'libfoo.so'), 'w').write('test')
374+
guess = eb.make_module_req()
375+
if get_module_syntax() == 'Tcl':
376+
self.assertTrue(re.search(r"^prepend-path\s+CMAKE_LIBRARY_PATH\s+\$root/lib64$", guess, re.M))
377+
elif get_module_syntax() == 'Lua':
378+
self.assertTrue('prepend_path("CMAKE_LIBRARY_PATH", pathJoin(root, "lib64"))' in guess)
379+
# -- With files in lib and lib64 symlinks to lib
380+
open(os.path.join(eb.installdir, 'lib', 'libfoo.so'), 'w').write('test')
381+
shutil.rmtree(os.path.join(eb.installdir, 'lib64'))
382+
os.symlink('lib', os.path.join(eb.installdir, 'lib64'))
383+
guess = eb.make_module_req()
384+
if get_module_syntax() == 'Tcl':
385+
self.assertFalse(re.search(r"^prepend-path\s+CMAKE_LIBRARY_PATH\s+\$root/lib64$", guess, re.M))
386+
elif get_module_syntax() == 'Lua':
387+
self.assertFalse('prepend_path("CMAKE_LIBRARY_PATH", pathJoin(root, "lib64"))' in guess)
388+
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+
364400
# check for behavior when a string value is used as dict value by make_module_req_guesses
365401
eb.make_module_req_guess = lambda: {'PATH': 'bin'}
366402
txt = eb.make_module_req()

test/framework/toy_build.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,7 @@ def test_toy_module_fulltxt(self):
12311231
r'',
12321232
r'conflict\("toy"\)',
12331233
r'',
1234+
r'prepend_path\("CMAKE_PREFIX_PATH", root\)',
12341235
r'prepend_path\("LD_LIBRARY_PATH", pathJoin\(root, "lib"\)\)',
12351236
r'prepend_path\("LIBRARY_PATH", pathJoin\(root, "lib"\)\)',
12361237
r'prepend_path\("PATH", pathJoin\(root, "bin"\)\)',
@@ -1268,6 +1269,7 @@ def test_toy_module_fulltxt(self):
12681269
r'',
12691270
r'conflict toy',
12701271
r'',
1272+
r'prepend-path CMAKE_PREFIX_PATH \$root',
12711273
r'prepend-path LD_LIBRARY_PATH \$root/lib',
12721274
r'prepend-path LIBRARY_PATH \$root/lib',
12731275
r'prepend-path PATH \$root/bin',

0 commit comments

Comments
 (0)