bpo-41100: Support macOS 11 and Apple Silicon (GH-22855)

Co-authored-by:  Lawrence D’Anna <lawrence_danna@apple.com>

* Add support for macOS 11 and Apple Silicon (aka arm64)
   
  As a side effect of this work use the system copy of libffi on macOS, and remove the vendored copy

* Support building on recent versions of macOS while deploying to older versions

  This allows building installers on macOS 11 while still supporting macOS 10.9.
This commit is contained in:
Ronald Oussoren 2020-11-08 10:05:27 +01:00 committed by GitHub
parent fd6f6fa403
commit 41761933c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1654 additions and 412 deletions

View File

@ -110,6 +110,26 @@ def _get_system_version():
return _SYSTEM_VERSION
_SYSTEM_VERSION_TUPLE = None
def _get_system_version_tuple():
"""
Return the macOS system version as a tuple
The return value is safe to use to compare
two version numbers.
"""
global _SYSTEM_VERSION_TUPLE
if _SYSTEM_VERSION_TUPLE is None:
osx_version = _get_system_version()
if osx_version:
try:
_SYSTEM_VERSION_TUPLE = tuple(int(i) for i in osx_version.split('.'))
except ValueError:
_SYSTEM_VERSION_TUPLE = ()
return _SYSTEM_VERSION_TUPLE
def _remove_original_values(_config_vars):
"""Remove original unmodified values for testing"""
# This is needed for higher-level cross-platform tests of get_platform.
@ -132,14 +152,18 @@ def _supports_universal_builds():
# builds, in particular -isysroot and -arch arguments to the compiler. This
# is in support of allowing 10.4 universal builds to run on 10.3.x systems.
osx_version = _get_system_version()
if osx_version:
try:
osx_version = tuple(int(i) for i in osx_version.split('.'))
except ValueError:
osx_version = ''
osx_version = _get_system_version_tuple()
return bool(osx_version >= (10, 4)) if osx_version else False
def _supports_arm64_builds():
"""Returns True if arm64 builds are supported on this system"""
# There are two sets of systems supporting macOS/arm64 builds:
# 1. macOS 11 and later, unconditionally
# 2. macOS 10.15 with Xcode 12.2 or later
# For now the second category is ignored.
osx_version = _get_system_version_tuple()
return osx_version >= (11, 0) if osx_version else False
def _find_appropriate_compiler(_config_vars):
"""Find appropriate C compiler for extension module builds"""
@ -331,6 +355,12 @@ def compiler_fixup(compiler_so, cc_args):
except ValueError:
break
elif not _supports_arm64_builds():
# Look for "-arch arm64" and drop that
for idx in range(len(compiler_so)):
if compiler_so[idx] == '-arch' and compiler_so[idx+1] == "arm64":
del compiler_so[idx:idx+2]
if 'ARCHFLAGS' in os.environ and not stripArch:
# User specified different -arch flags in the environ,
# see also distutils.sysconfig
@ -481,6 +511,8 @@ def get_platform_osx(_config_vars, osname, release, machine):
if len(archs) == 1:
machine = archs[0]
elif archs == ('arm64', 'x86_64'):
machine = 'universal2'
elif archs == ('i386', 'ppc'):
machine = 'fat'
elif archs == ('i386', 'x86_64'):

View File

@ -6,6 +6,11 @@ import os
from ctypes.macholib.framework import framework_info
from ctypes.macholib.dylib import dylib_info
from itertools import *
try:
from _ctypes import _dyld_shared_cache_contains_path
except ImportError:
def _dyld_shared_cache_contains_path(*args):
raise NotImplementedError
__all__ = [
'dyld_find', 'framework_find',
@ -122,8 +127,15 @@ def dyld_find(name, executable_path=None, env=None):
dyld_executable_path_search(name, executable_path),
dyld_default_search(name, env),
), env):
if os.path.isfile(path):
return path
try:
if _dyld_shared_cache_contains_path(path):
return path
except NotImplementedError:
pass
raise ValueError("dylib %s could not be found" % (name,))
def framework_find(fn, executable_path=None, env=None):

View File

@ -45,19 +45,22 @@ def find_lib(name):
class MachOTest(unittest.TestCase):
@unittest.skipUnless(sys.platform == "darwin", 'OSX-specific test')
def test_find(self):
self.assertEqual(find_lib('pthread'),
'/usr/lib/libSystem.B.dylib')
# On Mac OS 11, system dylibs are only present in the shared cache,
# so symlinks like libpthread.dylib -> libSystem.B.dylib will not
# be resolved by dyld_find
self.assertIn(find_lib('pthread'),
('/usr/lib/libSystem.B.dylib', '/usr/lib/libpthread.dylib'))
result = find_lib('z')
# Issue #21093: dyld default search path includes $HOME/lib and
# /usr/local/lib before /usr/lib, which caused test failures if
# a local copy of libz exists in one of them. Now ignore the head
# of the path.
self.assertRegex(result, r".*/lib/libz\..*.*\.dylib")
self.assertRegex(result, r".*/lib/libz.*\.dylib")
self.assertEqual(find_lib('IOKit'),
'/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit')
self.assertIn(find_lib('IOKit'),
('/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit',
'/System/Library/Frameworks/IOKit.framework/IOKit'))
if __name__ == "__main__":
unittest.main()

View File

@ -493,7 +493,7 @@ class BuildExtTestCase(TempdirManager,
# format the target value as defined in the Apple
# Availability Macros. We can't use the macro names since
# at least one value we test with will not exist yet.
if target[1] < 10:
if target[:2] < (10, 10):
# for 10.1 through 10.9.x -> "10n0"
target = '%02d%01d0' % target
else:

View File

@ -1036,6 +1036,7 @@ class BytesTest(BaseBytesTest, unittest.TestCase):
c_char_p)
PyBytes_FromFormat = pythonapi.PyBytes_FromFormat
PyBytes_FromFormat.argtypes = (c_char_p,)
PyBytes_FromFormat.restype = py_object
# basic tests

View File

@ -246,7 +246,7 @@ class PlatformTest(unittest.TestCase):
self.assertEqual(res[1], ('', '', ''))
if sys.byteorder == 'little':
self.assertIn(res[2], ('i386', 'x86_64'))
self.assertIn(res[2], ('i386', 'x86_64', 'arm64'))
else:
self.assertEqual(res[2], 'PowerPC')

View File

@ -1925,6 +1925,233 @@ class TestPosixSpawnP(unittest.TestCase, _PosixSpawnMixin):
assert_python_ok(*args, PATH=path)
@unittest.skipUnless(sys.platform == "darwin", "test weak linking on macOS")
class TestPosixWeaklinking(unittest.TestCase):
# These test cases verify that weak linking support on macOS works
# as expected. These cases only test new behaviour introduced by weak linking,
# regular behaviour is tested by the normal test cases.
#
# See the section on Weak Linking in Mac/README.txt for more information.
def setUp(self):
import sysconfig
import platform
config_vars = sysconfig.get_config_vars()
self.available = { nm for nm in config_vars if nm.startswith("HAVE_") and config_vars[nm] }
self.mac_ver = tuple(int(part) for part in platform.mac_ver()[0].split("."))
def _verify_available(self, name):
if name not in self.available:
raise unittest.SkipTest(f"{name} not weak-linked")
def test_pwritev(self):
self._verify_available("HAVE_PWRITEV")
if self.mac_ver >= (10, 16):
self.assertTrue(hasattr(os, "pwritev"), "os.pwritev is not available")
self.assertTrue(hasattr(os, "preadv"), "os.readv is not available")
else:
self.assertFalse(hasattr(os, "pwritev"), "os.pwritev is available")
self.assertFalse(hasattr(os, "preadv"), "os.readv is available")
def test_stat(self):
self._verify_available("HAVE_FSTATAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_FSTATAT", posix._have_functions)
else:
self.assertNotIn("HAVE_FSTATAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.stat("file", dir_fd=0)
def test_access(self):
self._verify_available("HAVE_FACCESSAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_FACCESSAT", posix._have_functions)
else:
self.assertNotIn("HAVE_FACCESSAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.access("file", os.R_OK, dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "follow_symlinks unavailable"):
os.access("file", os.R_OK, follow_symlinks=False)
with self.assertRaisesRegex(NotImplementedError, "effective_ids unavailable"):
os.access("file", os.R_OK, effective_ids=True)
def test_chmod(self):
self._verify_available("HAVE_FCHMODAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_FCHMODAT", posix._have_functions)
else:
self.assertNotIn("HAVE_FCHMODAT", posix._have_functions)
self.assertIn("HAVE_LCHMOD", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.chmod("file", 0o644, dir_fd=0)
def test_chown(self):
self._verify_available("HAVE_FCHOWNAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_FCHOWNAT", posix._have_functions)
else:
self.assertNotIn("HAVE_FCHOWNAT", posix._have_functions)
self.assertIn("HAVE_LCHOWN", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.chown("file", 0, 0, dir_fd=0)
def test_link(self):
self._verify_available("HAVE_LINKAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_LINKAT", posix._have_functions)
else:
self.assertNotIn("HAVE_LINKAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "src_dir_fd unavailable"):
os.link("source", "target", src_dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "dst_dir_fd unavailable"):
os.link("source", "target", dst_dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "src_dir_fd unavailable"):
os.link("source", "target", src_dir_fd=0, dst_dir_fd=0)
# issue 41355: !HAVE_LINKAT code path ignores the follow_symlinks flag
with os_helper.temp_dir() as base_path:
link_path = os.path.join(base_path, "link")
target_path = os.path.join(base_path, "target")
source_path = os.path.join(base_path, "source")
with open(source_path, "w") as fp:
fp.write("data")
os.symlink("target", link_path)
# Calling os.link should fail in the link(2) call, and
# should not reject *follow_symlinks* (to match the
# behaviour you'd get when building on a platform without
# linkat)
with self.assertRaises(FileExistsError):
os.link(source_path, link_path, follow_symlinks=True)
with self.assertRaises(FileExistsError):
os.link(source_path, link_path, follow_symlinks=False)
def test_listdir_scandir(self):
self._verify_available("HAVE_FDOPENDIR")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_FDOPENDIR", posix._have_functions)
else:
self.assertNotIn("HAVE_FDOPENDIR", posix._have_functions)
with self.assertRaisesRegex(TypeError, "listdir: path should be string, bytes, os.PathLike or None, not int"):
os.listdir(0)
with self.assertRaisesRegex(TypeError, "scandir: path should be string, bytes, os.PathLike or None, not int"):
os.scandir(0)
def test_mkdir(self):
self._verify_available("HAVE_MKDIRAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_MKDIRAT", posix._have_functions)
else:
self.assertNotIn("HAVE_MKDIRAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.mkdir("dir", dir_fd=0)
def test_rename_replace(self):
self._verify_available("HAVE_RENAMEAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_RENAMEAT", posix._have_functions)
else:
self.assertNotIn("HAVE_RENAMEAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
os.rename("a", "b", src_dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
os.rename("a", "b", dst_dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
os.replace("a", "b", src_dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "src_dir_fd and dst_dir_fd unavailable"):
os.replace("a", "b", dst_dir_fd=0)
def test_unlink_rmdir(self):
self._verify_available("HAVE_UNLINKAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_UNLINKAT", posix._have_functions)
else:
self.assertNotIn("HAVE_UNLINKAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.unlink("path", dir_fd=0)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.rmdir("path", dir_fd=0)
def test_open(self):
self._verify_available("HAVE_OPENAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_OPENAT", posix._have_functions)
else:
self.assertNotIn("HAVE_OPENAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.open("path", os.O_RDONLY, dir_fd=0)
def test_readlink(self):
self._verify_available("HAVE_READLINKAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_READLINKAT", posix._have_functions)
else:
self.assertNotIn("HAVE_READLINKAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.readlink("path", dir_fd=0)
def test_symlink(self):
self._verify_available("HAVE_SYMLINKAT")
if self.mac_ver >= (10, 10):
self.assertIn("HAVE_SYMLINKAT", posix._have_functions)
else:
self.assertNotIn("HAVE_SYMLINKAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.symlink("a", "b", dir_fd=0)
def test_utime(self):
self._verify_available("HAVE_FUTIMENS")
self._verify_available("HAVE_UTIMENSAT")
if self.mac_ver >= (10, 13):
self.assertIn("HAVE_FUTIMENS", posix._have_functions)
self.assertIn("HAVE_UTIMENSAT", posix._have_functions)
else:
self.assertNotIn("HAVE_FUTIMENS", posix._have_functions)
self.assertNotIn("HAVE_UTIMENSAT", posix._have_functions)
with self.assertRaisesRegex(NotImplementedError, "dir_fd unavailable"):
os.utime("path", dir_fd=0)
def test_main():
try:
support.run_unittest(
@ -1932,6 +2159,7 @@ def test_main():
PosixGroupsTester,
TestPosixSpawn,
TestPosixSpawnP,
TestPosixWeaklinking
)
finally:
support.reap_children()

View File

@ -1041,6 +1041,36 @@ class TestOldPyTime(CPyTimeTestCase, unittest.TestCase):
with self.assertRaises(ValueError):
pytime_object_to_timespec(float('nan'), time_rnd)
@unittest.skipUnless(sys.platform == "darwin", "test weak linking on macOS")
class TestTimeWeaklinking(unittest.TestCase):
# These test cases verify that weak linking support on macOS works
# as expected. These cases only test new behaviour introduced by weak linking,
# regular behaviour is tested by the normal test cases.
#
# See the section on Weak Linking in Mac/README.txt for more information.
def test_clock_functions(self):
import sysconfig
import platform
config_vars = sysconfig.get_config_vars()
var_name = "HAVE_CLOCK_GETTIME"
if var_name not in config_vars or not config_vars[var_name]:
raise unittest.SkipTest(f"{var_name} is not available")
mac_ver = tuple(int(x) for x in platform.mac_ver()[0].split("."))
clock_names = [
"CLOCK_MONOTONIC", "clock_gettime", "clock_gettime_ns", "clock_settime",
"clock_settime_ns", "clock_getres"]
if mac_ver >= (10, 12):
for name in clock_names:
self.assertTrue(hasattr(time, name), f"time.{name} is not available")
else:
for name in clock_names:
self.assertFalse(hasattr(time, name), f"time.{name} is available")
if __name__ == "__main__":
unittest.main()

View File

@ -2516,11 +2516,13 @@ class CAPITest(unittest.TestCase):
def test_from_format(self):
import_helper.import_module('ctypes')
from ctypes import (
c_char_p,
pythonapi, py_object, sizeof,
c_int, c_long, c_longlong, c_ssize_t,
c_uint, c_ulong, c_ulonglong, c_size_t, c_void_p)
name = "PyUnicode_FromFormat"
_PyUnicode_FromFormat = getattr(pythonapi, name)
_PyUnicode_FromFormat.argtypes = (c_char_p,)
_PyUnicode_FromFormat.restype = py_object
def PyUnicode_FromFormat(format, *args):

View File

@ -116,7 +116,8 @@ WORKDIR = "/tmp/_py"
DEPSRC = os.path.join(WORKDIR, 'third-party')
DEPSRC = os.path.expanduser('~/Universal/other-sources')
universal_opts_map = { '32-bit': ('i386', 'ppc',),
universal_opts_map = { 'universal2': ('arm64', 'x86_64'),
'32-bit': ('i386', 'ppc',),
'64-bit': ('x86_64', 'ppc64',),
'intel': ('i386', 'x86_64'),
'intel-32': ('i386',),
@ -124,6 +125,7 @@ universal_opts_map = { '32-bit': ('i386', 'ppc',),
'3-way': ('ppc', 'i386', 'x86_64'),
'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
default_target_map = {
'universal2': '10.9',
'64-bit': '10.5',
'3-way': '10.5',
'intel': '10.5',
@ -190,6 +192,27 @@ EXPECTED_SHARED_LIBS = {}
def internalTk():
return getDeptargetTuple() >= (10, 6)
def tweak_tcl_build(basedir, archList):
with open("Makefile", "r") as fp:
contents = fp.readlines()
# For reasons I don't understand the tcl configure script
# decides that some stdlib symbols aren't present, before
# deciding that strtod is broken.
new_contents = []
for line in contents:
if line.startswith("COMPAT_OBJS"):
# note: the space before strtod.o is intentional,
# the detection of a broken strtod results in
# "fixstrod.o" on this line.
for nm in ("strstr.o", "strtoul.o", " strtod.o"):
line = line.replace(nm, "")
new_contents.append(line)
with open("Makefile", "w") as fp:
fp.writelines(new_contents)
# List of names of third party software built with this installer.
# The names will be inserted into the rtf version of the License.
THIRD_PARTY_LIBS = []
@ -215,6 +238,9 @@ def library_recipes():
buildrecipe=build_universal_openssl,
configure=None,
install=None,
patches=[
"openssl-mac-arm64.patch",
],
),
])
@ -231,6 +257,7 @@ def library_recipes():
'--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
],
useLDFlags=False,
buildrecipe=tweak_tcl_build,
install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
"DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
"TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.6'%(getVersion())),
@ -801,6 +828,7 @@ def build_universal_openssl(basedir, archList):
arch_opts = {
"i386": ["darwin-i386-cc"],
"x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
"arm64": ["darwin64-arm64-cc"],
"ppc": ["darwin-ppc-cc"],
"ppc64": ["darwin64-ppc-cc"],
}

View File

@ -0,0 +1,41 @@
diff -ur openssl-1.1.1g-orig/Configurations/10-main.conf openssl-1.1.1g/Configurations/10-main.conf
--- openssl-1.1.1g-orig/Configurations/10-main.conf 2020-04-21 14:22:39.000000000 +0200
+++ openssl-1.1.1g/Configurations/10-main.conf 2020-07-26 12:21:32.000000000 +0200
@@ -1557,6 +1557,14 @@
bn_ops => "SIXTY_FOUR_BIT_LONG",
perlasm_scheme => "macosx",
},
+ "darwin64-arm64-cc" => {
+ inherit_from => [ "darwin-common", asm("aarch64_asm") ],
+ CFLAGS => add("-Wall"),
+ cflags => add("-arch arm64"),
+ lib_cppflags => add("-DL_ENDIAN"),
+ bn_ops => "SIXTY_FOUR_BIT_LONG",
+ perlasm_scheme => "ios64",
+ },
##### GNU Hurd
"hurd-x86" => {
diff -ur openssl-1.1.1g-orig/config openssl-1.1.1g/config
--- openssl-1.1.1g-orig/config 2020-04-21 14:22:39.000000000 +0200
+++ openssl-1.1.1g/config 2020-07-26 12:21:59.000000000 +0200
@@ -255,6 +255,9 @@
;;
x86_64)
echo "x86_64-apple-darwin${VERSION}"
+ ;;
+ arm64)
+ echo "arm64-apple-darwin${VERSION}"
;;
*)
echo "i686-apple-darwin${VERSION}"
@@ -497,6 +500,9 @@
else
OUT="darwin64-x86_64-cc"
fi ;;
+ x86_64-apple-darwin*)
+ OUT="darwin64-arm64-cc"
+ ;;
armv6+7-*-iphoneos)
__CNF_CFLAGS="$__CNF_CFLAGS -arch armv6 -arch armv7"
__CNF_CXXFLAGS="$__CNF_CXXFLAGS -arch armv6 -arch armv7"

View File

@ -120,6 +120,8 @@ support ppc (Xcode 4 on 10.6 and later systems). The flavor can be specified
using the configure option ``--with-universal-archs=VALUE``. The following
values are available:
* ``universal2``: ``arm64``, ``x86_64``
* ``intel``: ``i386``, ``x86_64``
* ``intel-32``: ``i386``
@ -155,6 +157,8 @@ following combinations of SDKs and universal-archs flavors are available:
* 10.15 and later SDKs support ``intel-64`` only
* 11.0 and later SDKs support ``universal2``
The makefile for a framework build will also install ``python3.x-32``
binaries when the universal architecture includes at least one 32-bit
architecture (that is, for all flavors but ``64-bit`` and ``intel-64``).
@ -352,6 +356,39 @@ A framework install also installs some applications in ``/Applications/Python X.
And lastly a framework installation installs files in ``/usr/local/bin``, all of
them symbolic links to files in ``/Library/Frameworks/Python.framework/Versions/X.Y/bin``.
Weak linking support
====================
The CPython sources support building with the latest SDK while targetting deployment
to macOS 10.9. This is done through weak linking of symbols introduced in macOS
10.10 or later and checking for their availability at runtime.
This requires the use of Apple's compiler toolchain on macOS 10.13 or later.
The basic implementation pattern is:
* ``HAVE_<FUNCTION>`` is a macro defined (or not) by the configure script
* ``HAVE_<FUNCTION>_RUNTIME`` is a macro defined in the relevant source
files. This expands to a call to ``__builtin_available`` when using
a new enough Apple compiler, and to a true value otherwise.
* Use ``HAVE_<FUNCTION>_RUNTIME`` before calling ``<function>``. This macro
*must* be used a the sole expression in an if statement::
if (HAVE_<FUNCTION>_RUNTIME) {
/* <function> is available */
}
Or:
if (HAVE_<FUNCTION>_RUNTIME) {} else {
/* <function> is not available */
}
Using other patterns (such as ``!HAVE_<FUNCTION>_RUNTIME``) is not supported
by Apple's compilers.
Resources
=========

View File

@ -95,9 +95,6 @@ setup_spawnattr(posix_spawnattr_t* spawnattr)
size_t count;
cpu_type_t cpu_types[1];
short flags = 0;
#ifdef __LP64__
int ch;
#endif
if ((errno = posix_spawnattr_init(spawnattr)) != 0) {
err(2, "posix_spawnattr_int");
@ -119,10 +116,16 @@ setup_spawnattr(posix_spawnattr_t* spawnattr)
#elif defined(__ppc__)
cpu_types[0] = CPU_TYPE_POWERPC;
#elif defined(__i386__)
cpu_types[0] = CPU_TYPE_X86;
#elif defined(__arm64__)
cpu_types[0] = CPU_TYPE_ARM64;
#else
# error "Unknown CPU"
#endif
if (posix_spawnattr_setbinpref_np(spawnattr, count,
@ -220,7 +223,8 @@ main(int argc, char **argv) {
/* We're weak-linking to posix-spawnv to ensure that
* an executable build on 10.5 can work on 10.4.
*/
if (posix_spawn != NULL) {
if (&posix_spawn != NULL) {
posix_spawnattr_t spawnattr = NULL;
setup_spawnattr(&spawnattr);

View File

@ -0,0 +1,8 @@
Add support for macOS 11 and Apple Silicon systems.
It is now possible to build "Universal 2" binaries using
"--enable-universalsdk --with-universal-archs=universal2".
Binaries build on later macOS versions can be deployed back to older
versions (tested up to macOS 10.9), when using the correct deployment
target. This is tested using Xcode 11 and later.

View File

@ -1,6 +1,8 @@
#include "Python.h"
#include "frameobject.h"
#include <stdbool.h>
#include <ffi.h>
#ifdef MS_WIN32
#include <windows.h>
@ -18,7 +20,7 @@ CThunkObject_dealloc(PyObject *myself)
Py_XDECREF(self->callable);
Py_XDECREF(self->restype);
if (self->pcl_write)
ffi_closure_free(self->pcl_write);
Py_ffi_closure_free(self->pcl_write);
PyObject_GC_Del(self);
}
@ -362,8 +364,7 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
assert(CThunk_CheckExact((PyObject *)p));
p->pcl_write = ffi_closure_alloc(sizeof(ffi_closure),
&p->pcl_exec);
p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec);
if (p->pcl_write == NULL) {
PyErr_NoMemory();
goto error;
@ -409,13 +410,35 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
"ffi_prep_cif failed with %d", result);
goto error;
}
#if defined(X86_DARWIN) || defined(POWERPC_DARWIN)
result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
#else
#if HAVE_FFI_PREP_CLOSURE_LOC
# if USING_APPLE_OS_LIBFFI
# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
# else
# define HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME 1
# endif
if (HAVE_FFI_PREP_CLOSURE_LOC_RUNTIME) {
result = ffi_prep_closure_loc(p->pcl_write, &p->cif, closure_fcn,
p,
p->pcl_exec);
} else
#endif
{
#if USING_APPLE_OS_LIBFFI && defined(__arm64__)
PyErr_Format(PyExc_NotImplementedError, "ffi_prep_closure_loc() is missing");
goto error;
#else
#ifdef MACOSX
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
result = ffi_prep_closure(p->pcl_write, &p->cif, closure_fcn, p);
#ifdef MACOSX
#pragma clang diagnostic pop
#endif
#endif
}
if (result != FFI_OK) {
PyErr_Format(PyExc_RuntimeError,
"ffi_prep_closure failed with %d", result);

View File

@ -57,6 +57,8 @@
#include "Python.h"
#include "structmember.h" // PyMemberDef
#include <stdbool.h>
#ifdef MS_WIN32
#include <windows.h>
#include <tchar.h>
@ -64,6 +66,10 @@
#include "ctypes_dlfcn.h"
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#ifdef MS_WIN32
#include <malloc.h>
#endif
@ -812,7 +818,8 @@ static int _call_function_pointer(int flags,
ffi_type **atypes,
ffi_type *restype,
void *resmem,
int argcount)
int argcount,
int argtypecount)
{
PyThreadState *_save = NULL; /* For Py_BLOCK_THREADS and Py_UNBLOCK_THREADS */
PyObject *error_object = NULL;
@ -835,6 +842,47 @@ static int _call_function_pointer(int flags,
if ((flags & FUNCFLAG_CDECL) == 0)
cc = FFI_STDCALL;
#endif
# if USING_APPLE_OS_LIBFFI
# define HAVE_FFI_PREP_CIF_VAR_RUNTIME __builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)
# elif HAVE_FFI_PREP_CIF_VAR
# define HAVE_FFI_PREP_CIF_VAR_RUNTIME true
# else
# define HAVE_FFI_PREP_CIF_VAR_RUNTIME false
# endif
/* Even on Apple-arm64 the calling convention for variadic functions conincides
* with the standard calling convention in the case that the function called
* only with its fixed arguments. Thus, we do not need a special flag to be
* set on variadic functions. We treat a function as variadic if it is called
* with a nonzero number of variadic arguments */
bool is_variadic = (argtypecount != 0 && argcount > argtypecount);
(void) is_variadic;
#if defined(__APPLE__) && defined(__arm64__)
if (is_variadic) {
if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
} else {
PyErr_SetString(PyExc_NotImplementedError, "ffi_prep_cif_var() is missing");
return -1;
}
}
#endif
#if HAVE_FFI_PREP_CIF_VAR
if (is_variadic) {
if (HAVE_FFI_PREP_CIF_VAR_RUNTIME) {
if (FFI_OK != ffi_prep_cif_var(&cif,
cc,
argtypecount,
argcount,
restype,
atypes)) {
PyErr_SetString(PyExc_RuntimeError,
"ffi_prep_cif_var failed");
return -1;
}
} else {
if (FFI_OK != ffi_prep_cif(&cif,
cc,
argcount,
@ -844,6 +892,21 @@ static int _call_function_pointer(int flags,
"ffi_prep_cif failed");
return -1;
}
}
} else
#endif
{
if (FFI_OK != ffi_prep_cif(&cif,
cc,
argcount,
restype,
atypes)) {
PyErr_SetString(PyExc_RuntimeError,
"ffi_prep_cif failed");
return -1;
}
}
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
error_object = _ctypes_get_errobj(&space);
@ -1212,9 +1275,8 @@ PyObject *_ctypes_callproc(PPROC pProc,
if (-1 == _call_function_pointer(flags, pProc, avalues, atypes,
rtype, resbuf,
Py_SAFE_DOWNCAST(argcount,
Py_ssize_t,
int)))
Py_SAFE_DOWNCAST(argcount, Py_ssize_t, int),
Py_SAFE_DOWNCAST(argtype_count, Py_ssize_t, int)))
goto cleanup;
#ifdef WORDS_BIGENDIAN
@ -1398,6 +1460,42 @@ copy_com_pointer(PyObject *self, PyObject *args)
}
#else
#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
static PyObject *py_dyld_shared_cache_contains_path(PyObject *self, PyObject *args)
{
PyObject *name, *name2;
char *name_str;
if (__builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)) {
int r;
if (!PyArg_ParseTuple(args, "O", &name))
return NULL;
if (name == Py_None)
Py_RETURN_FALSE;
if (PyUnicode_FSConverter(name, &name2) == 0)
return NULL;
name_str = PyBytes_AS_STRING(name2);
r = _dyld_shared_cache_contains_path(name_str);
Py_DECREF(name2);
if (r) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
} else {
PyErr_SetString(PyExc_NotImplementedError, "_dyld_shared_cache_contains_path symbol is missing");
return NULL;
}
}
#endif
static PyObject *py_dl_open(PyObject *self, PyObject *args)
{
PyObject *name, *name2;
@ -1887,6 +1985,8 @@ buffer_info(PyObject *self, PyObject *arg)
return Py_BuildValue("siN", dict->format, dict->ndim, shape);
}
PyMethodDef _ctypes_module_methods[] = {
{"get_errno", get_errno, METH_NOARGS},
{"set_errno", set_errno, METH_VARARGS},
@ -1908,6 +2008,9 @@ PyMethodDef _ctypes_module_methods[] = {
"dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library"},
{"dlclose", py_dl_close, METH_VARARGS, "dlclose a library"},
{"dlsym", py_dl_sym, METH_VARARGS, "find symbol in shared library"},
#endif
#ifdef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
{"_dyld_shared_cache_contains_path", py_dyld_shared_cache_contains_path, METH_VARARGS, "check if path is in the shared cache"},
#endif
{"alignment", align_func, METH_O, alignment_doc},
{"sizeof", sizeof_func, METH_O, sizeof_doc},

View File

@ -366,6 +366,14 @@ PyObject *_ctypes_get_errobj(int **pspace);
extern PyObject *ComError;
#endif
#if USING_MALLOC_CLOSURE_DOT_C
void Py_ffi_closure_free(void *p);
void *Py_ffi_closure_alloc(size_t size, void** codeloc);
#else
#define Py_ffi_closure_free ffi_closure_free
#define Py_ffi_closure_alloc ffi_closure_alloc
#endif
/*
Local Variables:
compile-command: "python setup.py -q build install --home ~"

View File

@ -89,16 +89,27 @@ static void more_core(void)
/******************************************************************/
/* put the item back into the free list */
void ffi_closure_free(void *p)
void Py_ffi_closure_free(void *p)
{
#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
ffi_closure_free(p);
return;
}
#endif
ITEM *item = (ITEM *)p;
item->next = free_list;
free_list = item;
}
/* return one item from the free list, allocating more if needed */
void *ffi_closure_alloc(size_t ignored, void** codeloc)
void *Py_ffi_closure_alloc(size_t size, void** codeloc)
{
#if USING_APPLE_OS_LIBFFI && HAVE_FFI_CLOSURE_ALLOC
if (__builtin_available(macos 10.15, ios 13, watchos 6, tvos 13, *)) {
return ffi_closure_alloc(size, codeloc);
}
#endif
ITEM *item;
if (!free_list)
more_core();

View File

@ -923,11 +923,7 @@ static PyStatus
calculate_program_macos(wchar_t **abs_path_p)
{
char execpath[MAXPATHLEN + 1];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
#else
unsigned long nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
#endif
/* On Mac OS X, if a script uses an interpreter of the form
"#!/opt/python2.3/bin/python", the kernel only passes "python"

File diff suppressed because it is too large Load Diff

View File

@ -51,6 +51,15 @@
#define _Py_tzname tzname
#endif
#if defined(__APPLE__ ) && defined(__has_builtin)
# if __has_builtin(__builtin_available)
# define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
# endif
#endif
#ifndef HAVE_CLOCK_GETTIME_RUNTIME
# define HAVE_CLOCK_GETTIME_RUNTIME 1
#endif
#define SEC_TO_NS (1000 * 1000 * 1000)
/* Forward declarations */
@ -149,6 +158,16 @@ perf_counter(_Py_clock_info_t *info)
}
#ifdef HAVE_CLOCK_GETTIME
#ifdef __APPLE__
/*
* The clock_* functions will be removed from the module
* dict entirely when the C API is not available.
*/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
#endif
static PyObject *
time_clock_gettime(PyObject *self, PyObject *args)
{
@ -297,6 +316,11 @@ PyDoc_STRVAR(clock_getres_doc,
"clock_getres(clk_id) -> floating point number\n\
\n\
Return the resolution (precision) of the specified clock clk_id.");
#ifdef __APPLE__
#pragma clang diagnostic pop
#endif
#endif /* HAVE_CLOCK_GETRES */
#ifdef HAVE_PTHREAD_GETCPUCLOCKID
@ -1162,6 +1186,9 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#if defined(HAVE_CLOCK_GETTIME) \
&& (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
struct timespec ts;
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#ifdef CLOCK_PROF
const clockid_t clk_id = CLOCK_PROF;
const char *function = "clock_gettime(CLOCK_PROF)";
@ -1188,6 +1215,7 @@ _PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
}
return 0;
}
}
#endif
/* getrusage(RUSAGE_SELF) */
@ -1390,6 +1418,16 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
#define HAVE_THREAD_TIME
#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
static int
_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
__attribute__((availability(macos, introduced=10.12)))
__attribute__((availability(ios, introduced=10.0)))
__attribute__((availability(tvos, introduced=10.0)))
__attribute__((availability(watchos, introduced=3.0)));
#endif
static int
_PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
{
@ -1421,6 +1459,15 @@ _PyTime_GetThreadTimeWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
#endif
#ifdef HAVE_THREAD_TIME
#ifdef __APPLE__
/*
* The clock_* functions will be removed from the module
* dict entirely when the C API is not available.
*/
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
#endif
static PyObject *
time_thread_time(PyObject *self, PyObject *unused)
{
@ -1451,6 +1498,11 @@ PyDoc_STRVAR(thread_time_ns_doc,
\n\
Thread time for profiling as nanoseconds:\n\
sum of the kernel and user-space CPU time.");
#ifdef __APPLE__
#pragma clang diagnostic pop
#endif
#endif
@ -1500,9 +1552,19 @@ time_get_clock_info(PyObject *self, PyObject *args)
}
#ifdef HAVE_THREAD_TIME
else if (strcmp(name, "thread_time") == 0) {
#ifdef __APPLE__
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#endif
if (_PyTime_GetThreadTimeWithInfo(&t, &info) < 0) {
return NULL;
}
#ifdef __APPLE__
} else {
PyErr_SetString(PyExc_ValueError, "unknown clock");
return NULL;
}
#endif
}
#endif
else {
@ -1783,28 +1845,73 @@ if it is -1, mktime() should guess based on the date and time.\n");
static int
time_exec(PyObject *module)
{
#if defined(__APPLE__) && defined(HAVE_CLOCK_GETTIME)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
/* pass: ^^^ cannot use '!' here */
} else {
PyObject* dct = PyModule_GetDict(module);
if (dct == NULL) {
return -1;
}
if (PyDict_DelItemString(dct, "clock_gettime") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_gettime_ns") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_settime") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_settime_ns") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "clock_getres") == -1) {
PyErr_Clear();
}
}
#endif
#if defined(__APPLE__) && defined(HAVE_THREAD_TIME)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
/* pass: ^^^ cannot use '!' here */
} else {
PyObject* dct = PyModule_GetDict(module);
if (PyDict_DelItemString(dct, "thread_time") == -1) {
PyErr_Clear();
}
if (PyDict_DelItemString(dct, "thread_time_ns") == -1) {
PyErr_Clear();
}
}
#endif
/* Set, or reset, module variables like time.timezone */
if (init_timezone(module) < 0) {
return -1;
}
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#ifdef CLOCK_REALTIME
if (PyModule_AddIntMacro(module, CLOCK_REALTIME) < 0) {
return -1;
}
#endif
#ifdef CLOCK_MONOTONIC
if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC) < 0) {
return -1;
}
#endif
#ifdef CLOCK_MONOTONIC_RAW
if (PyModule_AddIntMacro(module, CLOCK_MONOTONIC_RAW) < 0) {
return -1;
}
#endif
#ifdef CLOCK_HIGHRES
if (PyModule_AddIntMacro(module, CLOCK_HIGHRES) < 0) {
return -1;
@ -1815,6 +1922,7 @@ time_exec(PyObject *module)
return -1;
}
#endif
#ifdef CLOCK_THREAD_CPUTIME_ID
if (PyModule_AddIntMacro(module, CLOCK_THREAD_CPUTIME_ID) < 0) {
return -1;
@ -1841,10 +1949,12 @@ time_exec(PyObject *module)
}
#endif
#ifdef CLOCK_UPTIME_RAW
if (PyModule_AddIntMacro(module, CLOCK_UPTIME_RAW) < 0) {
return -1;
}
#endif
}
#endif /* defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES) */

View File

@ -25,6 +25,16 @@
# include <sanitizer/msan_interface.h>
#endif
#if defined(__APPLE__) && defined(__has_builtin)
# if __has_builtin(__builtin_available)
# define HAVE_GETENTRYPY_GETRANDOM_RUNTIME __builtin_available(macOS 10.12, iOS 10.10, tvOS 10.0, watchOS 3.0, *)
# endif
#endif
#ifndef HAVE_GETENTRYPY_GETRANDOM_RUNTIME
# define HAVE_GETENTRYPY_GETRANDOM_RUNTIME 1
#endif
#ifdef Py_DEBUG
int _Py_HashSecret_Initialized = 0;
#else
@ -208,6 +218,16 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
error.
getentropy() is retried if it failed with EINTR: interrupted by a signal. */
#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
static int
py_getentropy(char *buffer, Py_ssize_t size, int raise)
__attribute__((availability(macos,introduced=10.12)))
__attribute__((availability(ios,introduced=10.0)))
__attribute__((availability(tvos,introduced=10.0)))
__attribute__((availability(watchos,introduced=3.0)));
#endif
static int
py_getentropy(char *buffer, Py_ssize_t size, int raise)
{
@ -498,6 +518,7 @@ pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
#else
#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
if (HAVE_GETENTRYPY_GETRANDOM_RUNTIME) {
#ifdef PY_GETRANDOM
res = py_getrandom(buffer, size, blocking, raise);
#else
@ -511,6 +532,7 @@ pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
}
/* getrandom() or getentropy() function is not available: failed with
ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
} /* end of availability block */
#endif
return dev_urandom(buffer, size, raise);

View File

@ -5,6 +5,12 @@
#if defined(__APPLE__)
#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
#if defined(__APPLE__) && defined(__has_builtin)
# if __has_builtin(__builtin_available)
# define HAVE_CLOCK_GETTIME_RUNTIME __builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)
# endif
#endif
#endif
#define _PyTime_check_mul_overflow(a, b) \
@ -683,15 +689,22 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
#else /* MS_WINDOWS */
int err;
#ifdef HAVE_CLOCK_GETTIME
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
#else
#endif
#if !defined(HAVE_CLOCK_GETTIME) || defined(__APPLE__)
struct timeval tv;
#endif
assert(info == NULL || raise);
#ifdef HAVE_CLOCK_GETTIME
#ifdef HAVE_CLOCK_GETTIME_RUNTIME
if (HAVE_CLOCK_GETTIME_RUNTIME) {
#endif
err = clock_gettime(CLOCK_REALTIME, &ts);
if (err) {
if (raise) {
@ -715,7 +728,14 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
info->resolution = 1e-9;
}
}
#else /* HAVE_CLOCK_GETTIME */
#ifdef HAVE_CLOCK_GETTIME_RUNTIME
} else {
#endif
#endif
#if !defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GETTIME_RUNTIME)
/* test gettimeofday() */
err = gettimeofday(&tv, (struct timezone *)NULL);
@ -735,6 +755,11 @@ pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
info->monotonic = 0;
info->adjustable = 1;
}
#if defined(HAVE_CLOCK_GETTIME_RUNTIME) && defined(HAVE_CLOCK_GETTIME)
} /* end of availibity block */
#endif
#endif /* !HAVE_CLOCK_GETTIME */
#endif /* !MS_WINDOWS */
return 0;

45
configure vendored
View File

@ -1511,8 +1511,8 @@ Optional Packages:
specify the kind of universal binary that should be
created. this option is only valid when
--enable-universalsdk is set; options are:
("32-bit", "64-bit", "3-way", "intel", "intel-32",
"intel-64", or "all") see Mac/README.rst
("universal2", "32-bit", "64-bit", "3-way", "intel",
"intel-32", "intel-64", or "all") see Mac/README.rst
--with-framework-name=FRAMEWORK
specify the name for the python framework on macOS
only valid when --enable-framework is set. see
@ -7002,7 +7002,7 @@ fi
# The -arch flags for universal builds on OSX
# The -arch flags for universal builds on macOS
UNIVERSAL_ARCH_FLAGS=
@ -7529,6 +7529,11 @@ $as_echo "$CC" >&6; }
LIPO_32BIT_FLAGS="-extract ppc7400 -extract i386"
ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
;;
universal2)
UNIVERSAL_ARCH_FLAGS="-arch arm64 -arch x86_64"
LIPO_32BIT_FLAGS=""
ARCH_RUN_32BIT="true"
;;
intel)
UNIVERSAL_ARCH_FLAGS="-arch i386 -arch x86_64"
LIPO_32BIT_FLAGS="-extract i386"
@ -7550,7 +7555,7 @@ $as_echo "$CC" >&6; }
ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
;;
*)
as_fn_error $? "proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way" "$LINENO" 5
as_fn_error $? "proper usage is --with-universal-arch=universal2|32-bit|64-bit|all|intel|3-way" "$LINENO" 5
;;
esac
@ -9382,7 +9387,7 @@ fi
MACOSX_DEFAULT_ARCH="ppc"
;;
*)
as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5
as_fn_error $? "Unexpected output of 'arch' on macOS" "$LINENO" 5
;;
esac
else
@ -9393,8 +9398,11 @@ fi
ppc)
MACOSX_DEFAULT_ARCH="ppc64"
;;
arm64)
MACOSX_DEFAULT_ARCH="arm64"
;;
*)
as_fn_error $? "Unexpected output of 'arch' on OSX" "$LINENO" 5
as_fn_error $? "Unexpected output of 'arch' on macOS" "$LINENO" 5
;;
esac
@ -12029,6 +12037,31 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _dyld_shared_cache_contains_path" >&5
$as_echo_n "checking for _dyld_shared_cache_contains_path... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <mach-o/dyld.h>
int
main ()
{
void *x=_dyld_shared_cache_contains_path
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
$as_echo "#define HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext

View File

@ -218,7 +218,7 @@ AC_ARG_WITH(universal-archs,
AS_HELP_STRING([--with-universal-archs=ARCH],
[specify the kind of universal binary that should be created. this option is
only valid when --enable-universalsdk is set; options are:
("32-bit", "64-bit", "3-way", "intel", "intel-32", "intel-64", or "all")
("universal2", "32-bit", "64-bit", "3-way", "intel", "intel-32", "intel-64", or "all")
see Mac/README.rst]),
[
UNIVERSAL_ARCHS="$withval"
@ -1597,7 +1597,7 @@ AC_SUBST(BASECFLAGS)
AC_SUBST(CFLAGS_NODIST)
AC_SUBST(LDFLAGS_NODIST)
# The -arch flags for universal builds on OSX
# The -arch flags for universal builds on macOS
UNIVERSAL_ARCH_FLAGS=
AC_SUBST(UNIVERSAL_ARCH_FLAGS)
@ -1898,6 +1898,11 @@ yes)
LIPO_32BIT_FLAGS="-extract ppc7400 -extract i386"
ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
;;
universal2)
UNIVERSAL_ARCH_FLAGS="-arch arm64 -arch x86_64"
LIPO_32BIT_FLAGS=""
ARCH_RUN_32BIT="true"
;;
intel)
UNIVERSAL_ARCH_FLAGS="-arch i386 -arch x86_64"
LIPO_32BIT_FLAGS="-extract i386"
@ -1919,7 +1924,7 @@ yes)
ARCH_RUN_32BIT="/usr/bin/arch -i386 -ppc"
;;
*)
AC_MSG_ERROR([proper usage is --with-universal-arch=32-bit|64-bit|all|intel|3-way])
AC_MSG_ERROR([proper usage is --with-universal-arch=universal2|32-bit|64-bit|all|intel|3-way])
;;
esac
@ -2489,7 +2494,7 @@ case $ac_sys_system/$ac_sys_release in
MACOSX_DEFAULT_ARCH="ppc"
;;
*)
AC_MSG_ERROR([Unexpected output of 'arch' on OSX])
AC_MSG_ERROR([Unexpected output of 'arch' on macOS])
;;
esac
else
@ -2500,8 +2505,11 @@ case $ac_sys_system/$ac_sys_release in
ppc)
MACOSX_DEFAULT_ARCH="ppc64"
;;
arm64)
MACOSX_DEFAULT_ARCH="arm64"
;;
*)
AC_MSG_ERROR([Unexpected output of 'arch' on OSX])
AC_MSG_ERROR([Unexpected output of 'arch' on macOS])
;;
esac
@ -3774,6 +3782,12 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)
])
AC_MSG_CHECKING(for _dyld_shared_cache_contains_path)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mach-o/dyld.h>]], [[void *x=_dyld_shared_cache_contains_path]])],
[AC_DEFINE(HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH, 1, Define if you have the '_dyld_shared_cache_contains_path' function.)
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)
])
AC_MSG_CHECKING(for memfd_create)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[

View File

@ -784,6 +784,9 @@
/* Define if you have the 'prlimit' functions. */
#undef HAVE_PRLIMIT
/* Define if you have the '_dyld_shared_cache_contains_path' function. */
#undef HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH
/* Define to 1 if you have the <process.h> header file. */
#undef HAVE_PROCESS_H

View File

@ -239,6 +239,13 @@ def is_macosx_sdk_path(path):
or path.startswith('/Library/') )
def grep_headers_for(function, headers):
for header in headers:
with open(header, 'r') as f:
if function in f.read():
return True
return False
def find_file(filename, std_dirs, paths):
"""Searches for the directory where a given file is located,
and returns a possibly-empty list of additional directories, or None
@ -2105,43 +2112,17 @@ class PyBuildExt(build_ext):
library_dirs=added_lib_dirs))
return True
def configure_ctypes_darwin(self, ext):
# Darwin (OS X) uses preconfigured files, in
# the Modules/_ctypes/libffi_osx directory.
ffi_srcdir = os.path.abspath(os.path.join(self.srcdir, 'Modules',
'_ctypes', 'libffi_osx'))
sources = [os.path.join(ffi_srcdir, p)
for p in ['ffi.c',
'x86/darwin64.S',
'x86/x86-darwin.S',
'x86/x86-ffi_darwin.c',
'x86/x86-ffi64.c',
'powerpc/ppc-darwin.S',
'powerpc/ppc-darwin_closure.S',
'powerpc/ppc-ffi_darwin.c',
'powerpc/ppc64-darwin_closure.S',
]]
# Add .S (preprocessed assembly) to C compiler source extensions.
self.compiler.src_extensions.append('.S')
include_dirs = [os.path.join(ffi_srcdir, 'include'),
os.path.join(ffi_srcdir, 'powerpc')]
ext.include_dirs.extend(include_dirs)
ext.sources.extend(sources)
return True
def configure_ctypes(self, ext):
if not self.use_system_libffi:
if MACOS:
return self.configure_ctypes_darwin(ext)
print('INFO: Could not locate ffi libs and/or headers')
return False
return True
def detect_ctypes(self):
# Thomas Heller's _ctypes module
self.use_system_libffi = False
if (not sysconfig.get_config_var("LIBFFI_INCLUDEDIR") and MACOS):
self.use_system_libffi = True
else:
self.use_system_libffi = '--with-system-ffi' in sysconfig.get_config_var("CONFIG_ARGS")
include_dirs = []
extra_compile_args = ['-DPy_BUILD_CORE_MODULE']
extra_link_args = []
@ -2154,11 +2135,9 @@ class PyBuildExt(build_ext):
if MACOS:
sources.append('_ctypes/malloc_closure.c')
sources.append('_ctypes/darwin/dlfcn_simple.c')
extra_compile_args.append('-DUSING_MALLOC_CLOSURE_DOT_C=1')
extra_compile_args.append('-DMACOSX')
include_dirs.append('_ctypes/darwin')
# XXX Is this still needed?
# extra_link_args.extend(['-read_only_relocs', 'warning'])
elif HOST_PLATFORM == 'sunos5':
# XXX This shouldn't be necessary; it appears that some
@ -2188,31 +2167,48 @@ class PyBuildExt(build_ext):
sources=['_ctypes/_ctypes_test.c'],
libraries=['m']))
ffi_inc = sysconfig.get_config_var("LIBFFI_INCLUDEDIR")
ffi_lib = None
ffi_inc_dirs = self.inc_dirs.copy()
if MACOS:
if '--with-system-ffi' not in sysconfig.get_config_var("CONFIG_ARGS"):
return
ffi_in_sdk = os.path.join(macosx_sdk_root(), "usr/include/ffi")
if not ffi_inc:
if os.path.exists(ffi_in_sdk):
ext.extra_compile_args.append("-DUSING_APPLE_OS_LIBFFI=1")
ffi_inc = ffi_in_sdk
ffi_lib = 'ffi'
else:
# OS X 10.5 comes with libffi.dylib; the include files are
# in /usr/include/ffi
ffi_inc_dirs.append('/usr/include/ffi')
ffi_inc = [sysconfig.get_config_var("LIBFFI_INCLUDEDIR")]
if not ffi_inc or ffi_inc[0] == '':
ffi_inc = find_file('ffi.h', [], ffi_inc_dirs)
if ffi_inc is not None:
ffi_h = ffi_inc[0] + '/ffi.h'
if not ffi_inc:
found = find_file('ffi.h', [], ffi_inc_dirs)
if found:
ffi_inc = found[0]
if ffi_inc:
ffi_h = ffi_inc + '/ffi.h'
if not os.path.exists(ffi_h):
ffi_inc = None
print('Header file {} does not exist'.format(ffi_h))
ffi_lib = None
if ffi_inc is not None:
if ffi_lib is None and ffi_inc:
for lib_name in ('ffi', 'ffi_pic'):
if (self.compiler.find_library_file(self.lib_dirs, lib_name)):
ffi_lib = lib_name
break
if ffi_inc and ffi_lib:
ext.include_dirs.extend(ffi_inc)
ffi_headers = glob(os.path.join(ffi_inc, '*.h'))
if grep_headers_for('ffi_prep_cif_var', ffi_headers):
ext.extra_compile_args.append("-DHAVE_FFI_PREP_CIF_VAR=1")
if grep_headers_for('ffi_prep_closure_loc', ffi_headers):
ext.extra_compile_args.append("-DHAVE_FFI_PREP_CLOSURE_LOC=1")
if grep_headers_for('ffi_closure_alloc', ffi_headers):
ext.extra_compile_args.append("-DHAVE_FFI_CLOSURE_ALLOC=1")
ext.include_dirs.append(ffi_inc)
ext.libraries.append(ffi_lib)
self.use_system_libffi = True