diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py index d73416be21..ee9fb6884e 100644 --- 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]} + 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, []) - 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 + try: + check_call(args, stdout=PIPE, stderr=PIPE) + except Exception as e: + tty.debug(e) + return False - 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 + return True - # 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] + # 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)] - check_call([config_path] + config_args[config_name], - stdout=PIPE, stderr=PIPE) + # If there are no files to be patched, return early + if not to_be_patched: + return - 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) + # 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) - raise RuntimeError('Failed to find suitable ' + config_file) + # 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):