From a481087695346828db4d5f9e7f489e15a54630cc Mon Sep 17 00:00:00 2001 From: Toyohisa Kameyama Date: Fri, 16 Oct 2020 18:30:06 +0900 Subject: 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 | 125 +++++++++++++---------------- 1 file changed, 57 insertions(+), 68 deletions(-) (limited to 'lib') 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 @@ class AutotoolsPackage(PackageBase): """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 @@ class AutotoolsPackage(PackageBase): 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): -- cgit v1.2.3-60-g2f50