Repository: spack/spack

Issue:
Spack ABI check doesn't work properly with Apple gcc 4.2.1 (which is clang)
libdwarf failure when building it with elfutils for elf. I tried to install 'libdwarf@20130207%gcc@4.2.1'
and encountered an issue. I believe this is because of the recent changes @lee218llnl did with elf. Stack trace is below and gave multiple examples of how to cause the issue.


```
./bin/spack install libdwarf@20130207%gcc@4.2.1
==> Error: ProcessError: Command exited with status 1:
    '/Users/friedt2/wip/spack/var/spack/stage/elfutils-0.168-nbsfzimkr72rsdiavgwcbilsg7whg6gw/elfutils-0.168/configure' '--prefix=/Users/friedt2/wip/spack/opt/spack/darwin-elcapitan-x86_64/gcc-4.2.1/elfutils-0.168-nbsfzimkr72rsdiavgwcbilsg7whg6gw' '--enable-maintainer-mode'
/Users/friedt2/wip/spack/lib/spack/spack/build_systems/autotools.py:264, in configure:
     257      def configure(self, spec, prefix):
     258          """Runs configure with the arguments specified in :py:meth:`.configure_args`
     259          and an appropriately set prefix.
     260          """
     261          options = ['--prefix={0}'.format(prefix)] + self.configure_args()
     262  
     263          with working_dir(self.build_directory, create=True):
  >> 264              inspect.getmodule(self).configure(*options)
```
```
./bin/spack install libdwarf@20130207%gcc@4.2.1 ^libelf

spack/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'
```
```
./bin/spack install libdwarf@20130207%gcc@4.2.1 ^libelf

KeyError: 'No such spec in database! libelf@0.8.13%gcc@4.2.1 arch=darwin-elcapitan-x86_64'
```
@lee218llnl Should we set the default elf implementation *back*  to libelf


Discussion:
@kielfriedt perhaps we should try to debug this rather than work around it. Based on the errors you get, I'm not seeing how this could be related to the libelf/elfutils modification. For the configure error, can you send the config.log output?

For the second one, can you send the output of the command "gcc --print-file-name=libstdc++.dylib". I don't have a Mac, so I am not going to be able to reproduce the problem.

The third one may be a concretization issue that would be beyond me and again is something I can't reproduce myself. I'll ask others though, particularly @tgamblin.

Resolving patch:
diff --git a/lib/spack/spack/abi.py b/lib/spack/spack/abi.py
--- 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
--- 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
--- 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(


Test patch:
diff --git a/lib/spack/spack/test/cmd/test_compiler_cmd.py b/lib/spack/spack/test/cmd/test_compiler_cmd.py
--- 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)