build_environment: verify compiler executables exist are are accessible (#17260)
* build_environment: verify compiler executables exist and are accessible * fix existing tests * test compiler executable verification
This commit is contained in:
parent
1ed035def6
commit
c39983eb1a
@ -198,6 +198,9 @@ def set_compiler_environment_variables(pkg, env):
|
|||||||
compiler = pkg.compiler
|
compiler = pkg.compiler
|
||||||
spec = pkg.spec
|
spec = pkg.spec
|
||||||
|
|
||||||
|
# Make sure the executables for this compiler exist
|
||||||
|
compiler.verify_executables()
|
||||||
|
|
||||||
# Set compiler variables used by CMake and autotools
|
# Set compiler variables used by CMake and autotools
|
||||||
assert all(key in compiler.link_paths for key in (
|
assert all(key in compiler.link_paths for key in (
|
||||||
'cc', 'cxx', 'f77', 'fc'))
|
'cc', 'cxx', 'f77', 'fc'))
|
||||||
|
@ -27,12 +27,6 @@
|
|||||||
__all__ = ['Compiler']
|
__all__ = ['Compiler']
|
||||||
|
|
||||||
|
|
||||||
def _verify_executables(*paths):
|
|
||||||
for path in paths:
|
|
||||||
if not os.path.isfile(path) and os.access(path, os.X_OK):
|
|
||||||
raise CompilerAccessError(path)
|
|
||||||
|
|
||||||
|
|
||||||
@llnl.util.lang.memoized
|
@llnl.util.lang.memoized
|
||||||
def get_compiler_version_output(compiler_path, version_arg, ignore_errors=()):
|
def get_compiler_version_output(compiler_path, version_arg, ignore_errors=()):
|
||||||
"""Invokes the compiler at a given path passing a single
|
"""Invokes the compiler at a given path passing a single
|
||||||
@ -271,20 +265,16 @@ def __init__(self, cspec, operating_system, target,
|
|||||||
self.extra_rpaths = extra_rpaths
|
self.extra_rpaths = extra_rpaths
|
||||||
self.enable_implicit_rpaths = enable_implicit_rpaths
|
self.enable_implicit_rpaths = enable_implicit_rpaths
|
||||||
|
|
||||||
def check(exe):
|
self.cc = paths[0]
|
||||||
if exe is None:
|
self.cxx = paths[1]
|
||||||
return None
|
self.f77 = None
|
||||||
_verify_executables(exe)
|
self.fc = None
|
||||||
return exe
|
|
||||||
|
|
||||||
self.cc = check(paths[0])
|
|
||||||
self.cxx = check(paths[1])
|
|
||||||
if len(paths) > 2:
|
if len(paths) > 2:
|
||||||
self.f77 = check(paths[2])
|
self.f77 = paths[2]
|
||||||
if len(paths) == 3:
|
if len(paths) == 3:
|
||||||
self.fc = self.f77
|
self.fc = self.f77
|
||||||
else:
|
else:
|
||||||
self.fc = check(paths[3])
|
self.fc = paths[3]
|
||||||
|
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
self.extra_rpaths = extra_rpaths or []
|
self.extra_rpaths = extra_rpaths or []
|
||||||
@ -298,6 +288,21 @@ def check(exe):
|
|||||||
if value is not None:
|
if value is not None:
|
||||||
self.flags[flag] = tokenize_flags(value)
|
self.flags[flag] = tokenize_flags(value)
|
||||||
|
|
||||||
|
def verify_executables(self):
|
||||||
|
"""Raise an error if any of the compiler executables is not valid.
|
||||||
|
|
||||||
|
This method confirms that for all of the compilers (cc, cxx, f77, fc)
|
||||||
|
that have paths, those paths exist and are executable by the current
|
||||||
|
user.
|
||||||
|
Raises a CompilerAccessError if any of the non-null paths for the
|
||||||
|
compiler are not accessible.
|
||||||
|
"""
|
||||||
|
missing = [cmp for cmp in (self.cc, self.cxx, self.f77, self.fc)
|
||||||
|
if cmp and not (os.path.isfile(cmp) and
|
||||||
|
os.access(cmp, os.X_OK))]
|
||||||
|
if missing:
|
||||||
|
raise CompilerAccessError(self, missing)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
return self.spec.version
|
return self.spec.version
|
||||||
@ -575,10 +580,10 @@ def _compiler_environment(self):
|
|||||||
|
|
||||||
|
|
||||||
class CompilerAccessError(spack.error.SpackError):
|
class CompilerAccessError(spack.error.SpackError):
|
||||||
|
def __init__(self, compiler, paths):
|
||||||
def __init__(self, path):
|
msg = "Compiler '%s' has executables that are missing" % compiler.spec
|
||||||
super(CompilerAccessError, self).__init__(
|
msg += " or are not executable: %s" % paths
|
||||||
"'%s' is not a valid compiler." % path)
|
super(CompilerAccessError, self).__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class InvalidCompilerError(spack.error.SpackError):
|
class InvalidCompilerError(spack.error.SpackError):
|
||||||
|
@ -823,3 +823,33 @@ class MockPackage(object):
|
|||||||
pkg = MockPackage()
|
pkg = MockPackage()
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
compiler.setup_custom_environment(pkg, env)
|
compiler.setup_custom_environment(pkg, env)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.enable_compiler_verification
|
||||||
|
def test_compiler_executable_verification_raises(tmpdir):
|
||||||
|
compiler = MockCompiler()
|
||||||
|
compiler.cc = '/this/path/does/not/exist'
|
||||||
|
|
||||||
|
with pytest.raises(spack.compiler.CompilerAccessError):
|
||||||
|
compiler.verify_executables()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.enable_compiler_verification
|
||||||
|
def test_compiler_executable_verification_success(tmpdir):
|
||||||
|
def prepare_executable(name):
|
||||||
|
real = str(tmpdir.join('cc').ensure())
|
||||||
|
fs.set_executable(real)
|
||||||
|
setattr(compiler, name, real)
|
||||||
|
|
||||||
|
# setup mock compiler with real paths
|
||||||
|
compiler = MockCompiler()
|
||||||
|
for name in ('cc', 'cxx', 'f77', 'fc'):
|
||||||
|
prepare_executable(name)
|
||||||
|
|
||||||
|
# testing that this doesn't raise an error because the paths exist and
|
||||||
|
# are executable
|
||||||
|
compiler.verify_executables()
|
||||||
|
|
||||||
|
# Test that null entries don't fail
|
||||||
|
compiler.cc = None
|
||||||
|
compiler.verify_executables()
|
||||||
|
@ -70,6 +70,25 @@ def clean_user_environment():
|
|||||||
ev.activate(active)
|
ev.activate(active)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Disable checks on compiler executable existence
|
||||||
|
#
|
||||||
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
|
def mock_compiler_executable_verification(request, monkeypatch):
|
||||||
|
"""Mock the compiler executable verification to allow missing executables.
|
||||||
|
|
||||||
|
This fixture can be disabled for tests of the compiler verification
|
||||||
|
functionality by::
|
||||||
|
|
||||||
|
@pytest.mark.enable_compiler_verification
|
||||||
|
|
||||||
|
If a test is marked in that way this is a no-op."""
|
||||||
|
if 'enable_compiler_verification' not in request.keywords:
|
||||||
|
monkeypatch.setattr(spack.compiler.Compiler,
|
||||||
|
'verify_executables',
|
||||||
|
lambda x: None)
|
||||||
|
|
||||||
|
|
||||||
# Hooks to add command line options or set other custom behaviors.
|
# Hooks to add command line options or set other custom behaviors.
|
||||||
# They must be placed here to be found by pytest. See:
|
# They must be placed here to be found by pytest. See:
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user