Compilers: use Compiler._real_version for flag version checks (#18179)

Compilers can have strange versions, as the version is provided by the user.  We know the real version internally, (by querying the compiler) so expose it as a property and use it in places we don't trust the user.  Eventually we'll refactor this with compilers as dependencies, but this is the best fix we've got for now.

- [x] Make `real_version` a property and cache the version returned by the compiler
- [x] Use `real_version` to make C++ language level flags work
This commit is contained in:
Greg Becker 2020-08-19 21:56:06 -07:00 committed by GitHub
parent 1650824ef5
commit ccf94ded67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 43 deletions

View File

@ -217,7 +217,7 @@ def optimization_flags(self, compiler):
if isinstance(compiler, spack.spec.CompilerSpec): if isinstance(compiler, spack.spec.CompilerSpec):
compiler = spack.compilers.compilers_for_spec(compiler).pop() compiler = spack.compilers.compilers_for_spec(compiler).pop()
try: try:
compiler_version = compiler.get_real_version() compiler_version = compiler.real_version
except spack.util.executable.ProcessError as e: except spack.util.executable.ProcessError as e:
# log this and just return compiler.version instead # log this and just return compiler.version instead
tty.debug(str(e)) tty.debug(str(e))

View File

@ -18,6 +18,7 @@
import spack.error import spack.error
import spack.spec import spack.spec
import spack.version
import spack.architecture import spack.architecture
import spack.util.executable import spack.util.executable
import spack.util.module_cmd import spack.util.module_cmd
@ -278,7 +279,8 @@ def __init__(self, cspec, operating_system, target,
self.target = target self.target = target
self.modules = modules or [] self.modules = modules or []
self.alias = alias self.alias = alias
self.extra_rpaths = extra_rpaths self.environment = environment or {}
self.extra_rpaths = extra_rpaths or []
self.enable_implicit_rpaths = enable_implicit_rpaths self.enable_implicit_rpaths = enable_implicit_rpaths
self.cc = paths[0] self.cc = paths[0]
@ -292,9 +294,6 @@ def __init__(self, cspec, operating_system, target,
else: else:
self.fc = paths[3] self.fc = paths[3]
self.environment = environment
self.extra_rpaths = extra_rpaths or []
# Unfortunately have to make sure these params are accepted # Unfortunately have to make sure these params are accepted
# in the same order they are returned by sorted(flags) # in the same order they are returned by sorted(flags)
# in compilers/__init__.py # in compilers/__init__.py
@ -304,6 +303,10 @@ def __init__(self, cspec, operating_system, target,
if value is not None: if value is not None:
self.flags[flag] = tokenize_flags(value) self.flags[flag] = tokenize_flags(value)
# caching value for compiler reported version
# used for version checks for API, e.g. C++11 flag
self._real_version = None
def verify_executables(self): def verify_executables(self):
"""Raise an error if any of the compiler executables is not valid. """Raise an error if any of the compiler executables is not valid.
@ -333,6 +336,20 @@ def accessible_exe(exe):
def version(self): def version(self):
return self.spec.version return self.spec.version
@property
def real_version(self):
"""Executable reported compiler version used for API-determinations
E.g. C++11 flag checks.
"""
if not self._real_version:
try:
self._real_version = spack.version.Version(
self.get_real_version())
except spack.util.executable.ProcessError:
self._real_version = self.version
return self._real_version
def implicit_rpaths(self): def implicit_rpaths(self):
if self.enable_implicit_rpaths is False: if self.enable_implicit_rpaths is False:
return [] return []

View File

@ -38,7 +38,7 @@ def extract_version_from_output(cls, output):
def cxx11_flag(self): def cxx11_flag(self):
# Adapted from CMake's AppleClang-CXX rules # Adapted from CMake's AppleClang-CXX rules
# Spack's AppleClang detection only valid from Xcode >= 4.6 # Spack's AppleClang detection only valid from Xcode >= 4.6
if self.version < spack.version.ver('4.0.0'): if self.real_version < spack.version.ver('4.0.0'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", "Xcode < 4.0.0" self, "the C++11 standard", "cxx11_flag", "Xcode < 4.0.0"
) )
@ -47,11 +47,11 @@ def cxx11_flag(self):
@property @property
def cxx14_flag(self): def cxx14_flag(self):
# Adapted from CMake's rules for AppleClang # Adapted from CMake's rules for AppleClang
if self.version < spack.version.ver('5.1.0'): if self.real_version < spack.version.ver('5.1.0'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "Xcode < 5.1.0" self, "the C++14 standard", "cxx14_flag", "Xcode < 5.1.0"
) )
elif self.version < spack.version.ver('6.1.0'): elif self.real_version < spack.version.ver('6.1.0'):
return "-std=c++1y" return "-std=c++1y"
return "-std=c++14" return "-std=c++14"
@ -59,7 +59,7 @@ def cxx14_flag(self):
@property @property
def cxx17_flag(self): def cxx17_flag(self):
# Adapted from CMake's rules for AppleClang # Adapted from CMake's rules for AppleClang
if self.version < spack.version.ver('6.1.0'): if self.real_version < spack.version.ver('6.1.0'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "Xcode < 6.1.0" self, "the C++17 standard", "cxx17_flag", "Xcode < 6.1.0"
) )

View File

@ -34,7 +34,7 @@ class Cce(Compiler):
@property @property
def is_clang_based(self): def is_clang_based(self):
version = self.version version = self._real_version or self.version
return version >= ver('9.0') and 'classic' not in str(version) return version >= ver('9.0') and 'classic' not in str(version)
@property @property
@ -69,9 +69,9 @@ def cxx11_flag(self):
def c99_flag(self): def c99_flag(self):
if self.is_clang_based: if self.is_clang_based:
return '-std=c99' return '-std=c99'
elif self.version >= ver('8.4'): elif self.real_version >= ver('8.4'):
return '-h std=c99,noconform,gnu' return '-h std=c99,noconform,gnu'
elif self.version >= ver('8.1'): elif self.real_version >= ver('8.1'):
return '-h c99,noconform,gnu' return '-h c99,noconform,gnu'
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
'the C99 standard', 'the C99 standard',
@ -82,7 +82,7 @@ def c99_flag(self):
def c11_flag(self): def c11_flag(self):
if self.is_clang_based: if self.is_clang_based:
return '-std=c11' return '-std=c11'
elif self.version >= ver('8.5'): elif self.real_version >= ver('8.5'):
return '-h std=c11,noconform,gnu' return '-h std=c11,noconform,gnu'
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
'the C11 standard', 'the C11 standard',

View File

@ -90,7 +90,7 @@ def verbose_flag(self):
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.version < ver('3.3'): if self.real_version < ver('3.3'):
raise UnsupportedCompilerFlag( raise UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", "< 3.3" self, "the C++11 standard", "cxx11_flag", "< 3.3"
) )
@ -98,22 +98,22 @@ def cxx11_flag(self):
@property @property
def cxx14_flag(self): def cxx14_flag(self):
if self.version < ver('3.4'): if self.real_version < ver('3.4'):
raise UnsupportedCompilerFlag( raise UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "< 3.5" self, "the C++14 standard", "cxx14_flag", "< 3.5"
) )
elif self.version < ver('3.5'): elif self.real_version < ver('3.5'):
return "-std=c++1y" return "-std=c++1y"
return "-std=c++14" return "-std=c++14"
@property @property
def cxx17_flag(self): def cxx17_flag(self):
if self.version < ver('3.5'): if self.real_version < ver('3.5'):
raise UnsupportedCompilerFlag( raise UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "< 3.5" self, "the C++17 standard", "cxx17_flag", "< 3.5"
) )
elif self.version < ver('5.0'): elif self.real_version < ver('5.0'):
return "-std=c++1z" return "-std=c++1z"
return "-std=c++17" return "-std=c++17"
@ -124,7 +124,7 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.version < ver('6.1.0'): if self.real_version < ver('6.1.0'):
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
"the C11 standard", "the C11 standard",
"c11_flag", "c11_flag",

View File

@ -56,53 +56,53 @@ def openmp_flag(self):
@property @property
def cxx98_flag(self): def cxx98_flag(self):
if self.version < ver('6.0'): if self.real_version < ver('6.0'):
return "" return ""
else: else:
return "-std=c++98" return "-std=c++98"
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.version < ver('4.3'): if self.real_version < ver('4.3'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++11 standard", "cxx11_flag", " < 4.3") self, "the C++11 standard", "cxx11_flag", " < 4.3")
elif self.version < ver('4.7'): elif self.real_version < ver('4.7'):
return "-std=c++0x" return "-std=c++0x"
else: else:
return "-std=c++11" return "-std=c++11"
@property @property
def cxx14_flag(self): def cxx14_flag(self):
if self.version < ver('4.8'): if self.real_version < ver('4.8'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++14 standard", "cxx14_flag", "< 4.8") self, "the C++14 standard", "cxx14_flag", "< 4.8")
elif self.version < ver('4.9'): elif self.real_version < ver('4.9'):
return "-std=c++1y" return "-std=c++1y"
elif self.version < ver('6.0'): elif self.real_version < ver('6.0'):
return "-std=c++14" return "-std=c++14"
else: else:
return "" return ""
@property @property
def cxx17_flag(self): def cxx17_flag(self):
if self.version < ver('5.0'): if self.real_version < ver('5.0'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C++17 standard", "cxx17_flag", "< 5.0") self, "the C++17 standard", "cxx17_flag", "< 5.0")
elif self.version < ver('6.0'): elif self.real_version < ver('6.0'):
return "-std=c++1z" return "-std=c++1z"
else: else:
return "-std=c++17" return "-std=c++17"
@property @property
def c99_flag(self): def c99_flag(self):
if self.version < ver('4.5'): if self.real_version < ver('4.5'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C99 standard", "c99_flag", "< 4.5") self, "the C99 standard", "c99_flag", "< 4.5")
return "-std=c99" return "-std=c99"
@property @property
def c11_flag(self): def c11_flag(self):
if self.version < ver('4.7'): if self.real_version < ver('4.7'):
raise spack.compiler.UnsupportedCompilerFlag( raise spack.compiler.UnsupportedCompilerFlag(
self, "the C11 standard", "c11_flag", "< 4.7") self, "the C11 standard", "c11_flag", "< 4.7")
return "-std=c11" return "-std=c11"

View File

@ -48,20 +48,20 @@ def opt_flags(self):
@property @property
def openmp_flag(self): def openmp_flag(self):
if self.version < ver('16.0'): if self.real_version < ver('16.0'):
return "-openmp" return "-openmp"
else: else:
return "-qopenmp" return "-qopenmp"
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.version < ver('11.1'): if self.real_version < ver('11.1'):
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
"the C++11 standard", "the C++11 standard",
"cxx11_flag", "cxx11_flag",
"< 11.1") "< 11.1")
elif self.version < ver('13'): elif self.real_version < ver('13'):
return "-std=c++0x" return "-std=c++0x"
else: else:
return "-std=c++11" return "-std=c++11"
@ -69,19 +69,19 @@ def cxx11_flag(self):
@property @property
def cxx14_flag(self): def cxx14_flag(self):
# Adapted from CMake's Intel-CXX rules. # Adapted from CMake's Intel-CXX rules.
if self.version < ver('15'): if self.real_version < ver('15'):
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
"the C++14 standard", "the C++14 standard",
"cxx14_flag", "cxx14_flag",
"< 15") "< 15")
elif self.version < ver('15.0.2'): elif self.real_version < ver('15.0.2'):
return "-std=c++1y" return "-std=c++1y"
else: else:
return "-std=c++14" return "-std=c++14"
@property @property
def c99_flag(self): def c99_flag(self):
if self.version < ver('12'): if self.real_version < ver('12'):
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
"the C99 standard", "the C99 standard",
"c99_flag", "c99_flag",
@ -91,7 +91,7 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.version < ver('16'): if self.real_version < ver('16'):
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
"the C11 standard", "the C11 standard",
"c11_flag", "c11_flag",

View File

@ -73,7 +73,7 @@ def fc_pic_flag(self):
@property @property
def c99_flag(self): def c99_flag(self):
if self.version >= ver('12.10'): if self.real_version >= ver('12.10'):
return '-c99' return '-c99'
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
'the C99 standard', 'the C99 standard',
@ -82,7 +82,7 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.version >= ver('15.3'): if self.real_version >= ver('15.3'):
return '-c11' return '-c11'
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
'the C11 standard', 'the C11 standard',

View File

@ -47,7 +47,7 @@ def openmp_flag(self):
@property @property
def cxx11_flag(self): def cxx11_flag(self):
if self.version < ver('13.1'): if self.real_version < ver('13.1'):
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
"the C++11 standard", "the C++11 standard",
"cxx11_flag", "cxx11_flag",
@ -57,9 +57,9 @@ def cxx11_flag(self):
@property @property
def c99_flag(self): def c99_flag(self):
if self.version >= ver('13.1.1'): if self.real_version >= ver('13.1.1'):
return '-std=gnu99' return '-std=gnu99'
if self.version >= ver('10.1'): if self.real_version >= ver('10.1'):
return '-qlanglvl=extc99' return '-qlanglvl=extc99'
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
'the C99 standard', 'the C99 standard',
@ -68,9 +68,9 @@ def c99_flag(self):
@property @property
def c11_flag(self): def c11_flag(self):
if self.version >= ver('13.1.2'): if self.real_version >= ver('13.1.2'):
return '-std=gnu11' return '-std=gnu11'
if self.version >= ver('12.1'): if self.real_version >= ver('12.1'):
return '-qlanglvl=extc1x' return '-qlanglvl=extc1x'
raise UnsupportedCompilerFlag(self, raise UnsupportedCompilerFlag(self,
'the C11 standard', 'the C11 standard',

View File

@ -739,6 +739,41 @@ def _call(*args, **kwargs):
assert 'SPACK_TEST_CMP_ON' not in os.environ assert 'SPACK_TEST_CMP_ON' not in os.environ
def test_compiler_flags_use_real_version(working_env, monkeypatch, tmpdir):
# Create compiler
gcc = str(tmpdir.join('gcc'))
with open(gcc, 'w') as f:
f.write("""#!/bin/bash
echo "4.4.4"
""") # Version for which c++11 flag is -std=c++0x
fs.set_executable(gcc)
# Add compiler to config
compiler_info = {
'spec': 'gcc@foo',
'paths': {
'cc': gcc,
'cxx': None,
'f77': None,
'fc': None,
},
'flags': {},
'operating_system': 'fake',
'target': 'fake',
'modules': ['turn_on'],
'environment': {},
'extra_rpaths': [],
}
compiler_dict = {'compiler': compiler_info}
# Run and confirm output
compilers = spack.compilers.get_compilers([compiler_dict])
assert len(compilers) == 1
compiler = compilers[0]
flag = compiler.cxx11_flag
assert flag == '-std=c++0x'
def test_apple_clang_setup_environment(mock_executable, monkeypatch): def test_apple_clang_setup_environment(mock_executable, monkeypatch):
"""Test a code path that is taken only if the package uses """Test a code path that is taken only if the package uses
Xcode on MacOS. Xcode on MacOS.