From c67f8e4aa1d0d19c4c83d366d153d99428fcfb16 Mon Sep 17 00:00:00 2001
From: Todd Gamblin <tgamblin@llnl.gov>
Date: Fri, 21 Apr 2017 15:45:12 -0700
Subject: [PATCH] Fix ABI detection issues with macOS gcc. (#3854)

- gcc on macOS says it's version 4.2.1, but it's really clang, and it's
  actually the *same* clang as the system clang.

- It also doesn't respond with a full path when called with
  --print-file-name=libstdc++.dylib, which is expected from gcc in abi.py.
  Instead, it gives a relative path and _gcc_compiler_compare doesn't
  understand what to do with it.  This results in errors like:

  ```
  lib/spack/spack/abi.py, line 71, in _gcc_get_libstdcxx_version
      libpath = os.readlink(output.strip())
  OSError: [Errno 2] No such file or directory: 'libstdc++.dylib'
  ```

- This commit does two things:

  1. Ignore any gcc that's actually clang in abi.py.  We can probably do
     better than this, but it's not clear there is a need to, since we
     should handle the compiler as clang, not gcc.

  2. Don't auto-detect any "gcc" that is actually clang anymore.  Ignore
     it and expect people to use clang (which is the default macOS
     compiler anyway).

Users can still add fake gccs to their compilers.yaml if they want, but
it's discouraged.
---
 lib/spack/spack/abi.py                        |  9 ++++++++-
 lib/spack/spack/compiler.py                   |  7 ++++---
 lib/spack/spack/compilers/gcc.py              | 10 ++++++++++
 lib/spack/spack/test/cmd/test_compiler_cmd.py |  4 +---
 4 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/lib/spack/spack/abi.py b/lib/spack/spack/abi.py
index b3b1dd6d27..ad3cae6ee2 100644
--- a/lib/spack/spack/abi.py
+++ b/lib/spack/spack/abi.py
@@ -29,6 +29,7 @@
 from spack.build_environment import dso_suffix
 from spack.spec import CompilerSpec
 from spack.util.executable import Executable, ProcessError
+from spack.compilers.clang import Clang
 from llnl.util.lang import memoized
 
 
@@ -44,7 +45,7 @@ def architecture_compatible(self, parent, child):
     @memoized
     def _gcc_get_libstdcxx_version(self, version):
         """Returns gcc ABI compatibility info by getting the library version of
-           a compiler's libstdc++.so or libgcc_s.so"""
+           a compiler's libstdc++ or libgcc_s"""
         spec = CompilerSpec("gcc", version)
         compilers = spack.compilers.compilers_for_spec(spec)
         if not compilers:
@@ -62,6 +63,12 @@ def _gcc_get_libstdcxx_version(self, version):
         else:
             return None
         try:
+            # Some gcc's are actually clang and don't respond properly to
+            # --print-file-name (they just print the filename, not the
+            # full path).  Ignore these and expect them to be handled as clang.
+            if Clang.default_version(rungcc.exe[0]) != 'unknown':
+                return None
+
             output = rungcc("--print-file-name=%s" % libname,
                             return_output=True)
         except ProcessError:
diff --git a/lib/spack/spack/compiler.py b/lib/spack/spack/compiler.py
index 90af900d0d..bfce31a9a3 100644
--- a/lib/spack/spack/compiler.py
+++ b/lib/spack/spack/compiler.py
@@ -49,14 +49,15 @@ def _verify_executables(*paths):
 
 
 def get_compiler_version(compiler_path, version_arg, regex='(.*)'):
-    if compiler_path not in _version_cache:
+    key = (compiler_path, version_arg, regex)
+    if key not in _version_cache:
         compiler = Executable(compiler_path)
         output = compiler(version_arg, output=str, error=str)
 
         match = re.search(regex, output)
-        _version_cache[compiler_path] = match.group(1) if match else 'unknown'
+        _version_cache[key] = match.group(1) if match else 'unknown'
 
-    return _version_cache[compiler_path]
+    return _version_cache[key]
 
 
 def dumpversion(compiler_path):
diff --git a/lib/spack/spack/compilers/gcc.py b/lib/spack/spack/compilers/gcc.py
index 304f82a492..826ddbf432 100644
--- a/lib/spack/spack/compilers/gcc.py
+++ b/lib/spack/spack/compilers/gcc.py
@@ -87,6 +87,16 @@ def cxx17_flag(self):
     def pic_flag(self):
         return "-fPIC"
 
+    @classmethod
+    def default_version(cls, cc):
+        # Skip any gcc versions that are actually clang, like Apple's gcc.
+        # Returning "unknown" makes them not detected by default.
+        # Users can add these manually to compilers.yaml at their own risk.
+        if spack.compilers.clang.Clang.default_version(cc) != 'unknown':
+            return 'unknown'
+
+        return super(Gcc, cls).default_version(cc)
+
     @classmethod
     def fc_version(cls, fc):
         return get_compiler_version(
diff --git a/lib/spack/spack/test/cmd/test_compiler_cmd.py b/lib/spack/spack/test/cmd/test_compiler_cmd.py
index 842b64039e..b046cdb922 100644
--- a/lib/spack/spack/test/cmd/test_compiler_cmd.py
+++ b/lib/spack/spack/test/cmd/test_compiler_cmd.py
@@ -89,6 +89,4 @@ def test_compiler_add(self, mock_compiler_dir):
         # Ensure new compiler is in there
         new_compilers = set(spack.compilers.all_compiler_specs())
         new_compiler = new_compilers - old_compilers
-        assert new_compiler
-        assert sum(1 for c in new_compiler if
-                   c.version == Version(test_version)) > 0
+        assert any(c.version == Version(test_version) for c in new_compiler)
-- 
GitLab