Bug fix: module file path parsing (#9100)
Improve Spack's parsing of module show to eliminate some false positives (e.g. accepting MODULEPATH when it is in fact looking for PATH). This makes the following changes: * Updates the pattern searching for several paths to avoid the case where they are prefixes of unwanted paths * Adds a warning message when an extracted path doesn't exist (which may help catch future module parsing bugs faster) * Adds a test with the content mentioned in #9083
This commit is contained in:
parent
1e2b3b0768
commit
22fbb3dba7
@ -25,10 +25,13 @@
|
||||
import pytest
|
||||
import subprocess
|
||||
import os
|
||||
from spack.util.module_cmd import get_path_from_module
|
||||
from spack.util.module_cmd import get_argument_from_module_line
|
||||
from spack.util.module_cmd import get_module_cmd_from_bash
|
||||
from spack.util.module_cmd import get_module_cmd, ModuleError
|
||||
from spack.util.module_cmd import (
|
||||
get_path_from_module,
|
||||
get_path_from_module_contents,
|
||||
get_path_arg_from_module_line,
|
||||
get_module_cmd_from_bash,
|
||||
get_module_cmd,
|
||||
ModuleError)
|
||||
|
||||
|
||||
typeset_func = subprocess.Popen('module avail',
|
||||
@ -73,6 +76,26 @@ def test_get_path_from_module(save_env):
|
||||
assert path is None
|
||||
|
||||
|
||||
def test_get_path_from_module_contents():
|
||||
module_show_output = """
|
||||
os.environ["MODULEPATH"] = "/path/to/modules1:/path/to/modules2";
|
||||
----------------------------------------------------------------------------
|
||||
/root/cmake/3.9.2.lua:
|
||||
----------------------------------------------------------------------------
|
||||
help([[CMake Version 3.9.2
|
||||
]])
|
||||
whatis("Name: CMake")
|
||||
whatis("Version: 3.9.2")
|
||||
whatis("Category: Tools")
|
||||
whatis("URL: https://cmake.org/")
|
||||
prepend_path("PATH","/path/to/cmake-3.9.2/bin")
|
||||
prepend_path("MANPATH","/path/to/cmake/cmake-3.9.2/share/man")
|
||||
"""
|
||||
module_show_lines = module_show_output.split('\n')
|
||||
assert (get_path_from_module_contents(module_show_lines, 'cmake-3.9.2') ==
|
||||
'/path/to/cmake-3.9.2')
|
||||
|
||||
|
||||
def test_get_argument_from_module_line():
|
||||
lines = ['prepend-path LD_LIBRARY_PATH /lib/path',
|
||||
'prepend-path LD_LIBRARY_PATH /lib/path',
|
||||
@ -83,10 +106,10 @@ def test_get_argument_from_module_line():
|
||||
bad_lines = ['prepend_path(PATH,/lib/path)',
|
||||
'prepend-path (LD_LIBRARY_PATH) /lib/path']
|
||||
|
||||
assert all(get_argument_from_module_line(l) == '/lib/path' for l in lines)
|
||||
assert all(get_path_arg_from_module_line(l) == '/lib/path' for l in lines)
|
||||
for bl in bad_lines:
|
||||
with pytest.raises(ValueError):
|
||||
get_argument_from_module_line(bl)
|
||||
get_path_arg_from_module_line(bl)
|
||||
|
||||
|
||||
@pytest.mark.skipif(MODULE_NOT_DEFINED, reason='Depends on defined module fn')
|
||||
|
@ -148,7 +148,7 @@ def load_module(mod):
|
||||
exec(compile(load, '<string>', 'exec'))
|
||||
|
||||
|
||||
def get_argument_from_module_line(line):
|
||||
def get_path_arg_from_module_line(line):
|
||||
if '(' in line and ')' in line:
|
||||
# Determine which lua quote symbol is being used for the argument
|
||||
comma_index = line.index(',')
|
||||
@ -160,9 +160,15 @@ def get_argument_from_module_line(line):
|
||||
# Change error text to describe what is going on.
|
||||
raise ValueError("No lua quote symbol found in lmod module line.")
|
||||
words_and_symbols = line.split(lua_quote)
|
||||
return words_and_symbols[-2]
|
||||
path_arg = words_and_symbols[-2]
|
||||
else:
|
||||
return line.split()[2]
|
||||
path_arg = line.split()[2]
|
||||
|
||||
if not os.path.exists(path_arg):
|
||||
tty.warn("Extracted path from module does not exist:"
|
||||
"\n\tExtracted path: " + path_arg +
|
||||
"\n\tFull line: " + line)
|
||||
return path_arg
|
||||
|
||||
|
||||
def get_path_from_module(mod):
|
||||
@ -175,16 +181,22 @@ def get_path_from_module(mod):
|
||||
# Read the module
|
||||
text = modulecmd('show', mod, output=str, error=str).split('\n')
|
||||
|
||||
return get_path_from_module_contents(text, mod)
|
||||
|
||||
|
||||
def get_path_from_module_contents(text, module_name):
|
||||
# If it sets the LD_LIBRARY_PATH or CRAY_LD_LIBRARY_PATH, use that
|
||||
for line in text:
|
||||
if line.find('LD_LIBRARY_PATH') >= 0:
|
||||
path = get_argument_from_module_line(line)
|
||||
pattern = r'\WLD_LIBRARY_PATH'
|
||||
if re.search(pattern, line):
|
||||
path = get_path_arg_from_module_line(line)
|
||||
return path[:path.find('/lib')]
|
||||
|
||||
# If it lists its package directory, return that
|
||||
for line in text:
|
||||
if line.find(mod.upper() + '_DIR') >= 0:
|
||||
return get_argument_from_module_line(line)
|
||||
pattern = r'\W{0}_DIR'.format(module_name.upper())
|
||||
if re.search(pattern, line):
|
||||
return get_path_arg_from_module_line(line)
|
||||
|
||||
# If it lists a -rpath instruction, use that
|
||||
for line in text:
|
||||
@ -200,8 +212,9 @@ def get_path_from_module(mod):
|
||||
|
||||
# If it sets the PATH, use it
|
||||
for line in text:
|
||||
if line.find('PATH') >= 0:
|
||||
path = get_argument_from_module_line(line)
|
||||
pattern = r'\WPATH'
|
||||
if re.search(pattern, line):
|
||||
path = get_path_arg_from_module_line(line)
|
||||
return path[:path.find('/bin')]
|
||||
|
||||
# Unable to find module path
|
||||
|
Loading…
Reference in New Issue
Block a user