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 <massimiliano.culpo@gmail.com>
This commit is contained in:
Toyohisa Kameyama 2020-10-16 18:30:06 +09:00 committed by GitHub
parent cdeecf2507
commit a481087695
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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):