From ccf94ded67e688770dc8d0388d8f75d0d6641e1e Mon Sep 17 00:00:00 2001 From: Greg Becker Date: Wed, 19 Aug 2020 21:56:06 -0700 Subject: 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 --- lib/spack/spack/architecture.py | 2 +- lib/spack/spack/compiler.py | 25 +++++++++++++++++++---- lib/spack/spack/compilers/apple_clang.py | 8 ++++---- lib/spack/spack/compilers/cce.py | 8 ++++---- lib/spack/spack/compilers/clang.py | 12 +++++------ lib/spack/spack/compilers/gcc.py | 20 +++++++++--------- lib/spack/spack/compilers/intel.py | 14 ++++++------- lib/spack/spack/compilers/pgi.py | 4 ++-- lib/spack/spack/compilers/xl.py | 10 ++++----- lib/spack/spack/test/compilers/basics.py | 35 ++++++++++++++++++++++++++++++++ 10 files changed, 95 insertions(+), 43 deletions(-) diff --git a/lib/spack/spack/architecture.py b/lib/spack/spack/architecture.py index e4a20759cf..15561d436c 100644 --- a/lib/spack/spack/architecture.py +++ b/lib/spack/spack/architecture.py @@ -217,7 +217,7 @@ class Target(object): if isinstance(compiler, spack.spec.CompilerSpec): compiler = spack.compilers.compilers_for_spec(compiler).pop() try: - compiler_version = compiler.get_real_version() + compiler_version = compiler.real_version except spack.util.executable.ProcessError as e: # log this and just return compiler.version instead tty.debug(str(e)) diff --git a/lib/spack/spack/compiler.py b/lib/spack/spack/compiler.py index f1a9263c76..d498803633 100644 --- a/lib/spack/spack/compiler.py +++ b/lib/spack/spack/compiler.py @@ -18,6 +18,7 @@ import llnl.util.tty as tty import spack.error import spack.spec +import spack.version import spack.architecture import spack.util.executable import spack.util.module_cmd @@ -278,7 +279,8 @@ class Compiler(object): self.target = target self.modules = modules or [] 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.cc = paths[0] @@ -292,9 +294,6 @@ class Compiler(object): else: self.fc = paths[3] - self.environment = environment - self.extra_rpaths = extra_rpaths or [] - # Unfortunately have to make sure these params are accepted # in the same order they are returned by sorted(flags) # in compilers/__init__.py @@ -304,6 +303,10 @@ class Compiler(object): if value is not None: 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): """Raise an error if any of the compiler executables is not valid. @@ -333,6 +336,20 @@ class Compiler(object): def version(self): 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): if self.enable_implicit_rpaths is False: return [] diff --git a/lib/spack/spack/compilers/apple_clang.py b/lib/spack/spack/compilers/apple_clang.py index 63e0a9c42f..e03117ae05 100644 --- a/lib/spack/spack/compilers/apple_clang.py +++ b/lib/spack/spack/compilers/apple_clang.py @@ -38,7 +38,7 @@ class AppleClang(spack.compilers.clang.Clang): def cxx11_flag(self): # Adapted from CMake's AppleClang-CXX rules # 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( self, "the C++11 standard", "cxx11_flag", "Xcode < 4.0.0" ) @@ -47,11 +47,11 @@ class AppleClang(spack.compilers.clang.Clang): @property def cxx14_flag(self): # 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( 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++14" @@ -59,7 +59,7 @@ class AppleClang(spack.compilers.clang.Clang): @property def cxx17_flag(self): # 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( self, "the C++17 standard", "cxx17_flag", "Xcode < 6.1.0" ) diff --git a/lib/spack/spack/compilers/cce.py b/lib/spack/spack/compilers/cce.py index 55718c292a..a325bf3a45 100644 --- a/lib/spack/spack/compilers/cce.py +++ b/lib/spack/spack/compilers/cce.py @@ -34,7 +34,7 @@ class Cce(Compiler): @property 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) @property @@ -69,9 +69,9 @@ class Cce(Compiler): def c99_flag(self): if self.is_clang_based: return '-std=c99' - elif self.version >= ver('8.4'): + elif self.real_version >= ver('8.4'): return '-h std=c99,noconform,gnu' - elif self.version >= ver('8.1'): + elif self.real_version >= ver('8.1'): return '-h c99,noconform,gnu' raise UnsupportedCompilerFlag(self, 'the C99 standard', @@ -82,7 +82,7 @@ class Cce(Compiler): def c11_flag(self): if self.is_clang_based: return '-std=c11' - elif self.version >= ver('8.5'): + elif self.real_version >= ver('8.5'): return '-h std=c11,noconform,gnu' raise UnsupportedCompilerFlag(self, 'the C11 standard', diff --git a/lib/spack/spack/compilers/clang.py b/lib/spack/spack/compilers/clang.py index f158ff3276..2f8c7d43e3 100644 --- a/lib/spack/spack/compilers/clang.py +++ b/lib/spack/spack/compilers/clang.py @@ -90,7 +90,7 @@ class Clang(Compiler): @property def cxx11_flag(self): - if self.version < ver('3.3'): + if self.real_version < ver('3.3'): raise UnsupportedCompilerFlag( self, "the C++11 standard", "cxx11_flag", "< 3.3" ) @@ -98,22 +98,22 @@ class Clang(Compiler): @property def cxx14_flag(self): - if self.version < ver('3.4'): + if self.real_version < ver('3.4'): raise UnsupportedCompilerFlag( 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++14" @property def cxx17_flag(self): - if self.version < ver('3.5'): + if self.real_version < ver('3.5'): raise UnsupportedCompilerFlag( 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++17" @@ -124,7 +124,7 @@ class Clang(Compiler): @property def c11_flag(self): - if self.version < ver('6.1.0'): + if self.real_version < ver('6.1.0'): raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", diff --git a/lib/spack/spack/compilers/gcc.py b/lib/spack/spack/compilers/gcc.py index 8a19e7d1b5..02ee3e5db9 100644 --- a/lib/spack/spack/compilers/gcc.py +++ b/lib/spack/spack/compilers/gcc.py @@ -56,53 +56,53 @@ class Gcc(spack.compiler.Compiler): @property def cxx98_flag(self): - if self.version < ver('6.0'): + if self.real_version < ver('6.0'): return "" else: return "-std=c++98" @property def cxx11_flag(self): - if self.version < ver('4.3'): + if self.real_version < ver('4.3'): raise spack.compiler.UnsupportedCompilerFlag( 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" else: return "-std=c++11" @property def cxx14_flag(self): - if self.version < ver('4.8'): + if self.real_version < ver('4.8'): raise spack.compiler.UnsupportedCompilerFlag( 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" - elif self.version < ver('6.0'): + elif self.real_version < ver('6.0'): return "-std=c++14" else: return "" @property def cxx17_flag(self): - if self.version < ver('5.0'): + if self.real_version < ver('5.0'): raise spack.compiler.UnsupportedCompilerFlag( 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" else: return "-std=c++17" @property def c99_flag(self): - if self.version < ver('4.5'): + if self.real_version < ver('4.5'): raise spack.compiler.UnsupportedCompilerFlag( self, "the C99 standard", "c99_flag", "< 4.5") return "-std=c99" @property def c11_flag(self): - if self.version < ver('4.7'): + if self.real_version < ver('4.7'): raise spack.compiler.UnsupportedCompilerFlag( self, "the C11 standard", "c11_flag", "< 4.7") return "-std=c11" diff --git a/lib/spack/spack/compilers/intel.py b/lib/spack/spack/compilers/intel.py index b44ed73d6f..5e64a1439e 100644 --- a/lib/spack/spack/compilers/intel.py +++ b/lib/spack/spack/compilers/intel.py @@ -48,20 +48,20 @@ class Intel(Compiler): @property def openmp_flag(self): - if self.version < ver('16.0'): + if self.real_version < ver('16.0'): return "-openmp" else: return "-qopenmp" @property def cxx11_flag(self): - if self.version < ver('11.1'): + if self.real_version < ver('11.1'): raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", "< 11.1") - elif self.version < ver('13'): + elif self.real_version < ver('13'): return "-std=c++0x" else: return "-std=c++11" @@ -69,19 +69,19 @@ class Intel(Compiler): @property def cxx14_flag(self): # Adapted from CMake's Intel-CXX rules. - if self.version < ver('15'): + if self.real_version < ver('15'): raise UnsupportedCompilerFlag(self, "the C++14 standard", "cxx14_flag", "< 15") - elif self.version < ver('15.0.2'): + elif self.real_version < ver('15.0.2'): return "-std=c++1y" else: return "-std=c++14" @property def c99_flag(self): - if self.version < ver('12'): + if self.real_version < ver('12'): raise UnsupportedCompilerFlag(self, "the C99 standard", "c99_flag", @@ -91,7 +91,7 @@ class Intel(Compiler): @property def c11_flag(self): - if self.version < ver('16'): + if self.real_version < ver('16'): raise UnsupportedCompilerFlag(self, "the C11 standard", "c11_flag", diff --git a/lib/spack/spack/compilers/pgi.py b/lib/spack/spack/compilers/pgi.py index f782e1fc86..383281a9f4 100644 --- a/lib/spack/spack/compilers/pgi.py +++ b/lib/spack/spack/compilers/pgi.py @@ -73,7 +73,7 @@ class Pgi(Compiler): @property def c99_flag(self): - if self.version >= ver('12.10'): + if self.real_version >= ver('12.10'): return '-c99' raise UnsupportedCompilerFlag(self, 'the C99 standard', @@ -82,7 +82,7 @@ class Pgi(Compiler): @property def c11_flag(self): - if self.version >= ver('15.3'): + if self.real_version >= ver('15.3'): return '-c11' raise UnsupportedCompilerFlag(self, 'the C11 standard', diff --git a/lib/spack/spack/compilers/xl.py b/lib/spack/spack/compilers/xl.py index ce74ec47c1..66009646e3 100644 --- a/lib/spack/spack/compilers/xl.py +++ b/lib/spack/spack/compilers/xl.py @@ -47,7 +47,7 @@ class Xl(Compiler): @property def cxx11_flag(self): - if self.version < ver('13.1'): + if self.real_version < ver('13.1'): raise UnsupportedCompilerFlag(self, "the C++11 standard", "cxx11_flag", @@ -57,9 +57,9 @@ class Xl(Compiler): @property def c99_flag(self): - if self.version >= ver('13.1.1'): + if self.real_version >= ver('13.1.1'): return '-std=gnu99' - if self.version >= ver('10.1'): + if self.real_version >= ver('10.1'): return '-qlanglvl=extc99' raise UnsupportedCompilerFlag(self, 'the C99 standard', @@ -68,9 +68,9 @@ class Xl(Compiler): @property def c11_flag(self): - if self.version >= ver('13.1.2'): + if self.real_version >= ver('13.1.2'): return '-std=gnu11' - if self.version >= ver('12.1'): + if self.real_version >= ver('12.1'): return '-qlanglvl=extc1x' raise UnsupportedCompilerFlag(self, 'the C11 standard', diff --git a/lib/spack/spack/test/compilers/basics.py b/lib/spack/spack/test/compilers/basics.py index acc7c7e5f0..762a2e67aa 100644 --- a/lib/spack/spack/test/compilers/basics.py +++ b/lib/spack/spack/test/compilers/basics.py @@ -739,6 +739,41 @@ fi 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): """Test a code path that is taken only if the package uses Xcode on MacOS. -- cgit v1.2.3-70-g09d2