Repository: spack/spack

Issue:
viennarna: fixed build on aarch64
copy config.guess /src/RNAforester/g2-0.72/
Due to an old config.guess in /src/RNAforester/g2-0.72/
```
3 errors found in build log:
     387    /usr/convex/getsysinfo =
     388    
     389    UNAME_MACHINE = aarch64
     390    UNAME_RELEASE = 4.18.0-147.3.1.el8_1.aarch64
     391    UNAME_SYSTEM  = Linux
     392    UNAME_VERSION = #1 SMP Wed Nov 27 01:58:10 UTC 2019
  >> 393    configure: error: cannot guess build type; you must specify one
  >> 394    configure: error: ./configure failed for g2-0.72
  >> 395    configure: error: ./configure failed for src/RNAforester
```
Autotools modification for added a config.sub 
Currently the autotools script copies the config.guess file to packages with an old autotools install.
Whilst this resolves the issues with many packages some still depend upon the config.sub file.

This patch introduces a naive way of copying a corresponding config.sub file when copying a config.guess file - whilst assuming the locations are the same.

This fixes some package build issues on arm, such as argtable, where the updated config.guess is insufficient.

I have not included any additional error checking - if necessary and/or if you would rather this in its own routine, then please feel free to edit / reject.


Discussion:

@OliverPerks `flake8` is failing. Can you solve the issue and rebase?
@adamjstewart @h-denpo I would be tempted to close this stale PR but wanted to point you at this issue so that we can solve it together with the `config.guess` one raised in #18098 

Resolving patch:
diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py
--- a/lib/spack/spack/build_systems/autotools.py
+++ b/lib/spack/spack/build_systems/autotools.py
@@ -3,10 +3,9 @@
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 import inspect
+import itertools
 import os
 import os.path
-import shutil
-import stat
 from subprocess import PIPE
 from subprocess import check_call
 
@@ -103,8 +102,8 @@ def _do_patch_config_files(self):
         """Some packages ship with older config.guess/config.sub files and
         need to have these updated when installed on a newer architecture.
         In particular, config.guess fails for PPC64LE for version prior
-        to a 2013-06-10 build date (automake 1.13.4) and for ARM (aarch64)."""
-
+        to a 2013-06-10 build date (automake 1.13.4) and for ARM (aarch64).
+        """
         if not self.patch_config_files or (
                 not self.spec.satisfies('target=ppc64le:') and
                 not self.spec.satisfies('target=aarch64:')
@@ -121,70 +120,60 @@ def _do_patch_config_files(self):
         else:
             config_arch = 'local'
 
-        my_config_files = {'guess': None, 'sub': None}
-        config_files = {'guess': None, 'sub': None}
-        config_args = {'guess': [], 'sub': [config_arch]}
-
-        for config_name in config_files.keys():
-            config_file = 'config.{0}'.format(config_name)
-            if os.path.exists(config_file):
-                # First search the top-level source directory
-                my_config_files[config_name] = os.path.abspath(config_file)
-            else:
-                # Then search in all sub directories recursively.
-                # We would like to use AC_CONFIG_AUX_DIR, but not all packages
-                # ship with their configure.in or configure.ac.
-                config_path = next((os.path.abspath(os.path.join(r, f))
-                                    for r, ds, fs in os.walk('.') for f in fs
-                                    if f == config_file), None)
-                my_config_files[config_name] = config_path
-
-            if my_config_files[config_name] is not None:
-                try:
-                    config_path = my_config_files[config_name]
-                    check_call([config_path] + config_args[config_name],
-                               stdout=PIPE, stderr=PIPE)
-                    # The package's config file already runs OK, so just use it
-                    continue
-                except Exception as e:
-                    tty.debug(e)
-            else:
-                continue
-
-            # Look for a spack-installed automake package
-            if 'automake' in self.spec:
-                automake_dir = 'automake-' + str(self.spec['automake'].version)
-                automake_path = os.path.join(self.spec['automake'].prefix,
-                                             'share', automake_dir)
-                path = os.path.join(automake_path, config_file)
-                if os.path.exists(path):
-                    config_files[config_name] = path
-            # Look for the system's config.guess
-            if (config_files[config_name] is None and
-                    os.path.exists('/usr/share')):
-                automake_dir = [s for s in os.listdir('/usr/share') if
-                                "automake" in s]
-                if automake_dir:
-                    automake_path = os.path.join('/usr/share', automake_dir[0])
-                    path = os.path.join(automake_path, config_file)
-                    if os.path.exists(path):
-                        config_files[config_name] = path
-            if config_files[config_name] is not None:
-                try:
-                    config_path = config_files[config_name]
-                    my_config_path = my_config_files[config_name]
-
-                    check_call([config_path] + config_args[config_name],
-                               stdout=PIPE, stderr=PIPE)
-
-                    m = os.stat(my_config_path).st_mode & 0o777 | stat.S_IWUSR
-                    os.chmod(my_config_path, m)
-                    shutil.copyfile(config_path, my_config_path)
-                    continue
-                except Exception as e:
-                    tty.debug(e)
-
-            raise RuntimeError('Failed to find suitable ' + config_file)
+        def runs_ok(script_abs_path):
+            # Construct the list of arguments for the call
+            additional_args = {
+                'config.sub': [config_arch]
+            }
+            script_name = os.path.basename(script_abs_path)
+            args = [script_abs_path] + additional_args.get(script_name, [])
+
+            try:
+                check_call(args, stdout=PIPE, stderr=PIPE)
+            except Exception as e:
+                tty.debug(e)
+                return False
+
+            return True
+
+        # Compute the list of files that needs to be patched
+        search_dir = self.stage.path
+        to_be_patched = fs.find(
+            search_dir, files=['config.sub', 'config.guess'], recursive=True
+        )
+        to_be_patched = [f for f in to_be_patched if not runs_ok(f)]
+
+        # If there are no files to be patched, return early
+        if not to_be_patched:
+            return
+
+        # Directories where to search for files to be copied
+        # over the failing ones
+        good_file_dirs = ['/usr/share']
+        if 'automake' in self.spec:
+            good_file_dirs.insert(0, self.spec['automake'].prefix)
+
+        # List of files to be found in the directories above
+        to_be_found = list(set(os.path.basename(f) for f in to_be_patched))
+        substitutes = {}
+        for directory in good_file_dirs:
+            candidates = fs.find(directory, files=to_be_found, recursive=True)
+            candidates = [f for f in candidates if runs_ok(f)]
+            for name, good_files in itertools.groupby(
+                    candidates, key=os.path.basename
+            ):
+                substitutes[name] = next(good_files)
+                to_be_found.remove(name)
+
+        # Check that we found everything we needed
+        if to_be_found:
+            msg = 'Failed to find suitable substitutes for {0}'
+            raise RuntimeError(msg.format(', '.join(to_be_found)))
+
+        # Copy the good files over the bad ones
+        for abs_path in to_be_patched:
+            name = os.path.basename(abs_path)
+            fs.copy(substitutes[name], abs_path)
 
     @run_before('configure')
     def _set_autotools_environment_variables(self):


Test patch: