Fix Python 3.8 build on macOS (#13338)
This commit is contained in:
parent
4d99663ef9
commit
4ab63c17d5
@ -135,7 +135,7 @@ class Python(AutotoolsPackage):
|
|||||||
# a Mac.
|
# a Mac.
|
||||||
depends_on('libuuid', when='+uuid')
|
depends_on('libuuid', when='+uuid')
|
||||||
|
|
||||||
patch('tkinter.patch', when='@:2.8,3.3: platform=darwin')
|
patch('tkinter.patch', when='@:2.8,3.3:3.7 platform=darwin')
|
||||||
|
|
||||||
# Ensure that distutils chooses correct compiler option for RPATH on cray:
|
# Ensure that distutils chooses correct compiler option for RPATH on cray:
|
||||||
patch('cray-rpath-2.3.patch', when='@2.3:3.0.1 platform=cray')
|
patch('cray-rpath-2.3.patch', when='@2.3:3.0.1 platform=cray')
|
||||||
@ -190,7 +190,7 @@ def setup_build_environment(self, env):
|
|||||||
# Python v2.7 and v3.4+ (see https://bugs.python.org/issue1180) and
|
# Python v2.7 and v3.4+ (see https://bugs.python.org/issue1180) and
|
||||||
# adding support for ignoring user configuration will require
|
# adding support for ignoring user configuration will require
|
||||||
# significant changes to this package for other Python versions.
|
# significant changes to this package for other Python versions.
|
||||||
if not spec.satisfies('@2.7,3.4:'):
|
if not spec.satisfies('@2.7:2.8,3.4:'):
|
||||||
tty.warn(('Python v{0} may not install properly if Python '
|
tty.warn(('Python v{0} may not install properly if Python '
|
||||||
'user configurations are present.').format(self.version))
|
'user configurations are present.').format(self.version))
|
||||||
|
|
||||||
@ -290,21 +290,112 @@ def configure_args(self):
|
|||||||
return config_args
|
return config_args
|
||||||
|
|
||||||
@run_after('install')
|
@run_after('install')
|
||||||
def post_install(self):
|
def _save_distutil_vars(self):
|
||||||
|
"""
|
||||||
|
Run before changing automatically generated contents of the
|
||||||
|
_sysconfigdata.py, which is used by distutils to figure out what
|
||||||
|
executables to use while compiling and linking extensions. If we build
|
||||||
|
extensions with spack those executables should be spack's wrappers.
|
||||||
|
Spack partially covers this by setting environment variables that
|
||||||
|
are also accounted for by distutils. Currently there is one more known
|
||||||
|
variable that must be set, which is LDSHARED, so the method saves its
|
||||||
|
autogenerated value to pass it to the dependent package's setup script.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._distutil_vars = {}
|
||||||
|
|
||||||
|
input_filename = self.get_sysconfigdata_name()
|
||||||
|
input_dict = None
|
||||||
|
try:
|
||||||
|
with open(input_filename) as input_file:
|
||||||
|
match = re.search(r'build_time_vars\s*=\s*(?P<dict>{.*})',
|
||||||
|
input_file.read(),
|
||||||
|
flags=re.DOTALL)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
input_dict = ast.literal_eval(match.group('dict'))
|
||||||
|
except (IOError, SyntaxError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not input_dict:
|
||||||
|
tty.warn("Failed to find 'build_time_vars' dictionary in file "
|
||||||
|
"'%s'. This might cause the extensions that are "
|
||||||
|
"installed with distutils to call compilers directly "
|
||||||
|
"avoiding Spack's wrappers." % input_filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
for var_name in Python._DISTUTIL_VARS_TO_SAVE:
|
||||||
|
if var_name in input_dict:
|
||||||
|
self._distutil_vars[var_name] = input_dict[var_name]
|
||||||
|
else:
|
||||||
|
tty.warn("Failed to find key '%s' in 'build_time_vars' "
|
||||||
|
"dictionary in file '%s'. This might cause the "
|
||||||
|
"extensions that are installed with distutils to "
|
||||||
|
"call compilers directly avoiding Spack's wrappers."
|
||||||
|
% (var_name, input_filename))
|
||||||
|
|
||||||
|
if len(self._distutil_vars) > 0:
|
||||||
|
output_filename = None
|
||||||
|
try:
|
||||||
|
output_filename = join_path(
|
||||||
|
spack.store.layout.metadata_path(self.spec),
|
||||||
|
Python._DISTUTIL_CACHE_FILENAME)
|
||||||
|
with open(output_filename, 'w') as output_file:
|
||||||
|
sjson.dump(self._distutil_vars, output_file)
|
||||||
|
except Exception:
|
||||||
|
tty.warn("Failed to save metadata for distutils. This might "
|
||||||
|
"cause the extensions that are installed with "
|
||||||
|
"distutils to call compilers directly avoiding "
|
||||||
|
"Spack's wrappers.")
|
||||||
|
# We make the cache empty if we failed to save it to file
|
||||||
|
# to provide the same behaviour as in the case when the cache
|
||||||
|
# is initialized by the method load_distutils_data().
|
||||||
|
self._distutil_vars = {}
|
||||||
|
if output_filename:
|
||||||
|
force_remove(output_filename)
|
||||||
|
|
||||||
|
def _load_distutil_vars(self):
|
||||||
|
# We update and keep the cache unchanged only if the package is
|
||||||
|
# installed.
|
||||||
|
if not self._distutil_vars and self.installed:
|
||||||
|
try:
|
||||||
|
input_filename = join_path(
|
||||||
|
spack.store.layout.metadata_path(self.spec),
|
||||||
|
Python._DISTUTIL_CACHE_FILENAME)
|
||||||
|
if os.path.isfile(input_filename):
|
||||||
|
with open(input_filename) as input_file:
|
||||||
|
self._distutil_vars = sjson.load(input_file)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not self._distutil_vars:
|
||||||
|
self._distutil_vars = {}
|
||||||
|
|
||||||
|
return self._distutil_vars
|
||||||
|
|
||||||
|
@run_after('install')
|
||||||
|
def filter_compilers(self):
|
||||||
|
"""Run after install to tell the configuration files and Makefiles
|
||||||
|
to use the compilers that Spack built the package with.
|
||||||
|
|
||||||
|
If this isn't done, they'll have CC and CXX set to Spack's generic
|
||||||
|
cc and c++. We want them to be bound to whatever compiler
|
||||||
|
they were built with."""
|
||||||
|
|
||||||
|
kwargs = {'ignore_absent': True, 'backup': False, 'string': True}
|
||||||
|
|
||||||
|
filenames = [
|
||||||
|
self.get_sysconfigdata_name(), self.get_makefile_filename()
|
||||||
|
]
|
||||||
|
|
||||||
|
filter_file(spack_cc, self.compiler.cc, *filenames, **kwargs)
|
||||||
|
filter_file(spack_cxx, self.compiler.cxx, *filenames, **kwargs)
|
||||||
|
|
||||||
|
@run_after('install')
|
||||||
|
def symlink(self):
|
||||||
spec = self.spec
|
spec = self.spec
|
||||||
prefix = self.prefix
|
prefix = self.prefix
|
||||||
|
|
||||||
self.sysconfigfilename = '_sysconfigdata.py'
|
|
||||||
if spec.satisfies('@3.6:'):
|
|
||||||
# Python 3.6.0 renamed the sys config file
|
|
||||||
sc = 'import sysconfig; print(sysconfig._get_sysconfigdata_name())'
|
|
||||||
cf = self.command('-c', sc, output=str).strip()
|
|
||||||
self.sysconfigfilename = '{0}.py'.format(cf)
|
|
||||||
|
|
||||||
self._save_distutil_vars(prefix)
|
|
||||||
|
|
||||||
self.filter_compilers(prefix)
|
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# On OpenSuse 13, python uses <prefix>/lib64/python2.7/lib-dynload/*.so
|
# On OpenSuse 13, python uses <prefix>/lib64/python2.7/lib-dynload/*.so
|
||||||
# instead of <prefix>/lib/python2.7/lib-dynload/*.so. Oddly enough the
|
# instead of <prefix>/lib/python2.7/lib-dynload/*.so. Oddly enough the
|
||||||
@ -398,125 +489,6 @@ def import_tests(self):
|
|||||||
if '+uuid' in spec:
|
if '+uuid' in spec:
|
||||||
self.command('-c', 'import uuid')
|
self.command('-c', 'import uuid')
|
||||||
|
|
||||||
def _save_distutil_vars(self, prefix):
|
|
||||||
"""
|
|
||||||
Run before changing automatically generated contents of the
|
|
||||||
_sysconfigdata.py, which is used by distutils to figure out what
|
|
||||||
executables to use while compiling and linking extensions. If we build
|
|
||||||
extensions with spack those executables should be spack's wrappers.
|
|
||||||
Spack partially covers this by setting environment variables that
|
|
||||||
are also accounted for by distutils. Currently there is one more known
|
|
||||||
variable that must be set, which is LDSHARED, so the method saves its
|
|
||||||
autogenerated value to pass it to the dependent package's setup script.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._distutil_vars = {}
|
|
||||||
|
|
||||||
input_filename = None
|
|
||||||
for filename in [join_path(lib_dir,
|
|
||||||
'python{0}'.format(self.version.up_to(2)),
|
|
||||||
self.sysconfigfilename)
|
|
||||||
for lib_dir in [prefix.lib, prefix.lib64]]:
|
|
||||||
if os.path.isfile(filename):
|
|
||||||
input_filename = filename
|
|
||||||
break
|
|
||||||
if not input_filename:
|
|
||||||
return
|
|
||||||
|
|
||||||
input_dict = None
|
|
||||||
try:
|
|
||||||
with open(input_filename) as input_file:
|
|
||||||
match = re.search(r'build_time_vars\s*=\s*(?P<dict>{.*})',
|
|
||||||
input_file.read(),
|
|
||||||
flags=re.DOTALL)
|
|
||||||
|
|
||||||
if match:
|
|
||||||
input_dict = ast.literal_eval(match.group('dict'))
|
|
||||||
except (IOError, SyntaxError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not input_dict:
|
|
||||||
tty.warn("Failed to find 'build_time_vars' dictionary in file "
|
|
||||||
"'%s'. This might cause the extensions that are "
|
|
||||||
"installed with distutils to call compilers directly "
|
|
||||||
"avoiding Spack's wrappers." % input_filename)
|
|
||||||
return
|
|
||||||
|
|
||||||
for var_name in Python._DISTUTIL_VARS_TO_SAVE:
|
|
||||||
if var_name in input_dict:
|
|
||||||
self._distutil_vars[var_name] = input_dict[var_name]
|
|
||||||
else:
|
|
||||||
tty.warn("Failed to find key '%s' in 'build_time_vars' "
|
|
||||||
"dictionary in file '%s'. This might cause the "
|
|
||||||
"extensions that are installed with distutils to "
|
|
||||||
"call compilers directly avoiding Spack's wrappers."
|
|
||||||
% (var_name, input_filename))
|
|
||||||
|
|
||||||
if len(self._distutil_vars) > 0:
|
|
||||||
output_filename = None
|
|
||||||
try:
|
|
||||||
output_filename = join_path(
|
|
||||||
spack.store.layout.metadata_path(self.spec),
|
|
||||||
Python._DISTUTIL_CACHE_FILENAME)
|
|
||||||
with open(output_filename, 'w') as output_file:
|
|
||||||
sjson.dump(self._distutil_vars, output_file)
|
|
||||||
except Exception:
|
|
||||||
tty.warn("Failed to save metadata for distutils. This might "
|
|
||||||
"cause the extensions that are installed with "
|
|
||||||
"distutils to call compilers directly avoiding "
|
|
||||||
"Spack's wrappers.")
|
|
||||||
# We make the cache empty if we failed to save it to file
|
|
||||||
# to provide the same behaviour as in the case when the cache
|
|
||||||
# is initialized by the method load_distutils_data().
|
|
||||||
self._distutil_vars = {}
|
|
||||||
if output_filename:
|
|
||||||
force_remove(output_filename)
|
|
||||||
|
|
||||||
def _load_distutil_vars(self):
|
|
||||||
# We update and keep the cache unchanged only if the package is
|
|
||||||
# installed.
|
|
||||||
if not self._distutil_vars and self.installed:
|
|
||||||
try:
|
|
||||||
input_filename = join_path(
|
|
||||||
spack.store.layout.metadata_path(self.spec),
|
|
||||||
Python._DISTUTIL_CACHE_FILENAME)
|
|
||||||
if os.path.isfile(input_filename):
|
|
||||||
with open(input_filename) as input_file:
|
|
||||||
self._distutil_vars = sjson.load(input_file)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not self._distutil_vars:
|
|
||||||
self._distutil_vars = {}
|
|
||||||
|
|
||||||
return self._distutil_vars
|
|
||||||
|
|
||||||
def filter_compilers(self, prefix):
|
|
||||||
"""Run after install to tell the configuration files and Makefiles
|
|
||||||
to use the compilers that Spack built the package with.
|
|
||||||
|
|
||||||
If this isn't done, they'll have CC and CXX set to Spack's generic
|
|
||||||
cc and c++. We want them to be bound to whatever compiler
|
|
||||||
they were built with."""
|
|
||||||
|
|
||||||
kwargs = {'ignore_absent': True, 'backup': False, 'string': True}
|
|
||||||
|
|
||||||
lib_dirnames = [
|
|
||||||
join_path(lib_dir, 'python{0}'.format(self.version.up_to(2))) for
|
|
||||||
lib_dir in [prefix.lib, prefix.lib64]]
|
|
||||||
|
|
||||||
config_dirname = 'config-{0}m'.format(
|
|
||||||
self.version.up_to(2)) if self.spec.satisfies('@3:') else 'config'
|
|
||||||
|
|
||||||
rel_filenames = [self.sysconfigfilename,
|
|
||||||
join_path(config_dirname, 'Makefile')]
|
|
||||||
|
|
||||||
abs_filenames = [join_path(dirname, filename) for dirname in
|
|
||||||
lib_dirnames for filename in rel_filenames]
|
|
||||||
|
|
||||||
filter_file(env['CC'], self.compiler.cc, *abs_filenames, **kwargs)
|
|
||||||
filter_file(env['CXX'], self.compiler.cxx, *abs_filenames, **kwargs)
|
|
||||||
|
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
# Set up environment to make install easy for python extensions.
|
# Set up environment to make install easy for python extensions.
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
@ -579,7 +551,7 @@ def print_string(self, string):
|
|||||||
return 'print({0})'.format(string)
|
return 'print({0})'.format(string)
|
||||||
|
|
||||||
def get_config_var(self, key):
|
def get_config_var(self, key):
|
||||||
"""Returns the value of a single variable. Wrapper around
|
"""Return the value of a single variable. Wrapper around
|
||||||
``distutils.sysconfig.get_config_var()``."""
|
``distutils.sysconfig.get_config_var()``."""
|
||||||
|
|
||||||
cmd = 'from distutils.sysconfig import get_config_var; '
|
cmd = 'from distutils.sysconfig import get_config_var; '
|
||||||
@ -588,7 +560,7 @@ def get_config_var(self, key):
|
|||||||
return self.command('-c', cmd, output=str).strip()
|
return self.command('-c', cmd, output=str).strip()
|
||||||
|
|
||||||
def get_config_h_filename(self):
|
def get_config_h_filename(self):
|
||||||
"""Returns the full path name of the configuration header.
|
"""Return the full path name of the configuration header.
|
||||||
Wrapper around ``distutils.sysconfig.get_config_h_filename()``."""
|
Wrapper around ``distutils.sysconfig.get_config_h_filename()``."""
|
||||||
|
|
||||||
cmd = 'from distutils.sysconfig import get_config_h_filename; '
|
cmd = 'from distutils.sysconfig import get_config_h_filename; '
|
||||||
@ -596,6 +568,50 @@ def get_config_h_filename(self):
|
|||||||
|
|
||||||
return self.command('-c', cmd, output=str).strip()
|
return self.command('-c', cmd, output=str).strip()
|
||||||
|
|
||||||
|
def get_makefile_filename(self):
|
||||||
|
"""Return the full path name of ``Makefile`` used to build Python.
|
||||||
|
Wrapper around ``distutils.sysconfig.get_makefile_filename()``."""
|
||||||
|
|
||||||
|
cmd = 'from distutils.sysconfig import get_makefile_filename; '
|
||||||
|
cmd += self.print_string('get_makefile_filename()')
|
||||||
|
|
||||||
|
return self.command('-c', cmd, output=str).strip()
|
||||||
|
|
||||||
|
def get_python_inc(self):
|
||||||
|
"""Return the directory for either the general or platform-dependent C
|
||||||
|
include files. Wrapper around ``distutils.sysconfig.get_python_inc()``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cmd = 'from distutils.sysconfig import get_python_inc; '
|
||||||
|
cmd += self.print_string('get_python_inc()')
|
||||||
|
|
||||||
|
return self.command('-c', cmd, output=str).strip()
|
||||||
|
|
||||||
|
def get_python_lib(self):
|
||||||
|
"""Return the directory for either the general or platform-dependent
|
||||||
|
library installation. Wrapper around
|
||||||
|
``distutils.sysconfig.get_python_lib()``."""
|
||||||
|
|
||||||
|
cmd = 'from distutils.sysconfig import get_python_lib; '
|
||||||
|
cmd += self.print_string('get_python_lib()')
|
||||||
|
|
||||||
|
return self.command('-c', cmd, output=str).strip()
|
||||||
|
|
||||||
|
def get_sysconfigdata_name(self):
|
||||||
|
"""Return the full path name of the sysconfigdata file."""
|
||||||
|
|
||||||
|
libdest = self.get_config_var('LIBDEST')
|
||||||
|
|
||||||
|
filename = '_sysconfigdata.py'
|
||||||
|
if self.spec.satisfies('@3.6:'):
|
||||||
|
# Python 3.6.0 renamed the sys config file
|
||||||
|
cmd = 'from sysconfig import _get_sysconfigdata_name; '
|
||||||
|
cmd += self.print_string('_get_sysconfigdata_name()')
|
||||||
|
filename = self.command('-c', cmd, output=str).strip()
|
||||||
|
filename += '.py'
|
||||||
|
|
||||||
|
return join_path(libdest, filename)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def home(self):
|
def home(self):
|
||||||
"""Most of the time, ``PYTHONHOME`` is simply
|
"""Most of the time, ``PYTHONHOME`` is simply
|
||||||
|
Loading…
Reference in New Issue
Block a user