From a481087695346828db4d5f9e7f489e15a54630cc Mon Sep 17 00:00:00 2001 From: Toyohisa Kameyama Date: Fri, 16 Oct 2020 18:30:06 +0900 Subject: [PATCH] autotools: recursively patch config.guess and config.sub (#18347) Previously config.guess and config.sub were patched only in the root of the source path. This modification extend the previous behavior to patch every config.guess or config.sub file even in subfolders, if need be. Co-authored-by: Massimiliano Culpo --- lib/spack/spack/build_systems/autotools.py | 113 ++++++++++----------- 1 file changed, 51 insertions(+), 62 deletions(-) diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py index d73416be218..ee9fb6884e9 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):