diff --git a/Include/internal/pycore_faulthandler.h b/Include/internal/pycore_faulthandler.h index e6aec7745a..55a09a1970 100644 --- a/Include/internal/pycore_faulthandler.h +++ b/Include/internal/pycore_faulthandler.h @@ -12,6 +12,14 @@ extern "C" { # include #endif +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + +// tvOS and watchOS don't provide a number of important POSIX functions. +#if TARGET_OS_TV || TARGET_OS_WATCH +# undef HAVE_SIGALTSTACK +#endif /* TVOS || WATCHOS */ #ifndef MS_WINDOWS /* register() is useless on Windows, because only SIGSEGV, SIGABRT and diff --git a/Lib/_ios_support.py b/Lib/_ios_support.py new file mode 100644 index 0000000000..54ee81e49a --- /dev/null +++ b/Lib/_ios_support.py @@ -0,0 +1,36 @@ +from ctypes import cdll, c_void_p, c_char_p +from ctypes import util + + +def get_platform_ios(): + objc = cdll.LoadLibrary(util.find_library(b'objc')) + + objc.objc_getClass.restype = c_void_p + objc.objc_getClass.argtypes = [c_char_p] + objc.objc_msgSend.restype = c_void_p + objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + objc.sel_registerName.restype = c_void_p + objc.sel_registerName.argtypes = [c_char_p] + + UIDevice = c_void_p(objc.objc_getClass(b'UIDevice')) + SEL_currentDevice = c_void_p(objc.sel_registerName(b'currentDevice')) + device = c_void_p(objc.objc_msgSend(UIDevice, SEL_currentDevice)) + + SEL_systemVersion = c_void_p(objc.sel_registerName(b'systemVersion')) + systemVersion = c_void_p(objc.objc_msgSend(device, SEL_systemVersion)) + + SEL_systemName = c_void_p(objc.sel_registerName(b'systemName')) + systemName = c_void_p(objc.objc_msgSend(device, SEL_systemName)) + + SEL_model = c_void_p(objc.sel_registerName(b'model')) + systemModel = c_void_p(objc.objc_msgSend(device, SEL_model)) + + # UTF8String returns a const char*; + SEL_UTF8String = c_void_p(objc.sel_registerName(b'UTF8String')) + objc.objc_msgSend.restype = c_char_p + + system = objc.objc_msgSend(systemName, SEL_UTF8String).decode() + release = objc.objc_msgSend(systemVersion, SEL_UTF8String).decode() + model = objc.objc_msgSend(systemModel, SEL_UTF8String).decode() + + return system, release, model diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index c550883e7c..cd307fe7ce 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -67,7 +67,7 @@ def find_library(name): return fname return None -elif os.name == "posix" and sys.platform == "darwin": +elif os.name == "posix" and sys.platform in {'darwin', 'ios', 'tvos', 'watchos'}: from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): possible = ['lib%s.dylib' % name, diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 61dafc0f4c..5f9555af58 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -52,7 +52,7 @@ # Bootstrap-related code ###################################################### _CASE_INSENSITIVE_PLATFORMS_STR_KEY = 'win', -_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin' +_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos' _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) @@ -1694,6 +1694,71 @@ def __repr__(self): return f'FileFinder({self.path!r})' +class AppleFrameworkLoader(ExtensionFileLoader): + """A loader for modules that have been packaged as Apple Frameworks for + compatibility with Apple's App Store policies. + + For compatibility with the App Store, *all* binary modules must be in .dylibs, + contained in a Framework, in the ``Frameworks`` folder of the packaged app. If + you're trying to run "from foo import _bar", and _bar is implemented with the binary + module "foo/_bar.abi3.dylib" (or any other .dylib extension), this loader will look + for "{sys.executable}/Frameworks/foo__bar.framework/_bar.abi3.dylib" (forming the + package name by taking the full path of the library, and replacing ``/`` with + ``_``). The app packaging tool is responsible for putting the library in this + location. + + However, the ``__file__`` attribute of the _bar module will report as the original + location inside the ``foo`` directory. This so that code that depends on walking + directory trees will continue to work as expected based on the *original* file + location. + """ + def __init__(self, fullname, dylib_file, path): + super().__init__(fullname, dylib_file) + self.parent_paths = path + + def create_module(self, spec): + mod = super().create_module(spec) + if self.parent_paths: + for parent_path in self.parent_paths: + if _path_isdir(parent_path): + mod.__file__ = _path_join(parent_path, _path_split(self.path)[-1]) + continue + return mod + + +class AppleFrameworkFinder: + """A finder for modules that have been packaged as Apple Frameworks + for compatibility with Apple's App Store policies. + + See AppleFrameworkLoader for details. + """ + def __init__(self, path): + self.frameworks_path = path + + def find_spec(self, fullname, path, target=None): + + # Try to locate a framework with a binary in the format: + # /Frameworks/module_full_name.framework/module_full_name + framework_name = fullname + binary_file = _path_join(self.frameworks_path, f"{framework_name}.framework", framework_name) + _bootstrap._verbose_message('Looking for Apple Framework binary {}', binary_file) + + if _path_isfile(binary_file): + loader = AppleFrameworkLoader(fullname, binary_file, path) + return _bootstrap.spec_from_loader(fullname, loader) + + # Try to locate a framework with a dylib in the format: + # /Frameworks/module.full.name.framework/name.dylib + name = fullname.split(".")[-1] + for extension in EXTENSION_SUFFIXES: + dylib_file = _path_join(self.frameworks_path, f"{fullname}.framework", f"{name}{extension}") + _bootstrap._verbose_message('Looking for Apple Framework dylib {}', dylib_file) + if _path_isfile(dylib_file): + loader = AppleFrameworkLoader(fullname, dylib_file, path) + return _bootstrap.spec_from_loader(fullname, loader) + + return None + # Import setup ############################################################### def _fix_up_module(ns, name, pathname, cpathname=None): @@ -1743,3 +1808,7 @@ def _install(_bootstrap_module): supported_loaders = _get_supported_file_loaders() sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)]) sys.meta_path.append(PathFinder) + if sys.platform in {"ios", "tvos", "watchos"}: + frameworks_folder = _path_join(_path_split(sys.executable)[0], "Frameworks") + _bootstrap._verbose_message('Adding Apple Framework dylib finder at {}', frameworks_folder) + sys.meta_path.append(AppleFrameworkFinder(frameworks_folder)) diff --git a/Lib/platform.py b/Lib/platform.py index 7bb222088d..0a5ed0361e 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -496,6 +496,26 @@ def mac_ver(release='', versioninfo=('', '', ''), machine=''): # If that also doesn't work return the default values return release, versioninfo, machine +def iOS_ver(): + """Get iOS/tvOS version information, and return it as a + tuple (system, release, model). All tuple entries are strings. + """ + import _ios_support + return _ios_support.get_platform_ios() + +def is_simulator(): + """Determine if the current platform is a device simulator. + + Only useful when working with iOS, tvOS or watchOS, because + Apple provides simulator platforms for those devices. + + If the platform is actual hardware, returns False. Will also + return False for device *emulators*, which are indistinguishable + from actual devices because they are reproducing actual device + properties. + """ + return getattr(sys.implementation, "_simulator", False) + def _java_getprop(name, default): from java.lang import System @@ -652,7 +672,7 @@ def _syscmd_file(target, default=''): default in case the command should fail. """ - if sys.platform in ('dos', 'win32', 'win16'): + if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}: # XXX Others too ? return default @@ -814,6 +834,24 @@ def get_OpenVMS(): csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' + # On iOS, tvOS and watchOS, os.uname returns the architecture + # as uname.machine. On device it doesn't; but there's only + # on CPU architecture on device + def get_ios(): + if getattr(sys.implementation, "_simulator", False): + return os.uname().machine + return 'arm64' + + def get_tvos(): + if getattr(sys.implementation, "_simulator", False): + return os.uname().machine + return 'arm64' + + def get_watchos(): + if getattr(sys.implementation, "_simulator", False): + return os.uname().machine + return 'arm64_32' + def from_subprocess(): """ Fall back to `uname -p` @@ -968,6 +1006,15 @@ def uname(): system = 'Windows' release = 'Vista' + # Normalize responses on Apple mobile platforms + if sys.platform in {'ios', 'tvos'}: + system, release, model = iOS_ver() + + # On iOS/tvOS simulators, os.uname() reports the machine as something + # like "arm64" or "x86_64". + if getattr(sys.implementation, "_simulator", False): + machine = f'{model}Simulator' + vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' _uname_cache = uname_result(*map(_unknown_as_blank, vals)) @@ -1247,11 +1294,13 @@ def platform(aliased=False, terse=False): system, release, version = system_alias(system, release, version) if system == 'Darwin': - # macOS (darwin kernel) - macos_release = mac_ver()[0] - if macos_release: - system = 'macOS' - release = macos_release + if sys.platform in {'ios', 'tvos'}: + system, release, _ = iOS_ver() + else: + macos_release = mac_ver()[0] + if macos_release: + system = 'macOS' + release = macos_release if system == 'Windows': # MS platforms diff --git a/Lib/site.py b/Lib/site.py index 924b2460d9..9d99106819 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -304,6 +304,9 @@ def _get_path(userbase): if sys.platform == 'darwin' and sys._framework: return f'{userbase}/lib/python/site-packages' + elif sys.platform in ('ios', 'tvos', 'watchos'): + from sysconfig import get_path + return get_path('purelib', sys.platform) return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 1d17ae3608..531fb926fb 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -74,8 +74,8 @@ else: _mswindows = True -# wasm32-emscripten and wasm32-wasi do not support processes -_can_fork_exec = sys.platform not in {"emscripten", "wasi"} +# some platforms do not support processes +_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"} if _mswindows: import _winapi @@ -103,18 +103,22 @@ if _can_fork_exec: from _posixsubprocess import fork_exec as _fork_exec # used in methods that are called by __del__ - _waitpid = os.waitpid - _waitstatus_to_exitcode = os.waitstatus_to_exitcode - _WIFSTOPPED = os.WIFSTOPPED - _WSTOPSIG = os.WSTOPSIG - _WNOHANG = os.WNOHANG + class _del_safe: + waitpid = os.waitpid + waitstatus_to_exitcode = os.waitstatus_to_exitcode + WIFSTOPPED = os.WIFSTOPPED + WSTOPSIG = os.WSTOPSIG + WNOHANG = os.WNOHANG + ECHILD = errno.ECHILD else: - _fork_exec = None - _waitpid = None - _waitstatus_to_exitcode = None - _WIFSTOPPED = None - _WSTOPSIG = None - _WNOHANG = None + class _del_safe: + waitpid = None + waitstatus_to_exitcode = None + WIFSTOPPED = None + WSTOPSIG = None + WNOHANG = None + ECHILD = errno.ECHILD + import select import selectors @@ -1958,20 +1962,16 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, raise child_exception_type(err_msg) - def _handle_exitstatus(self, sts, - _waitstatus_to_exitcode=_waitstatus_to_exitcode, - _WIFSTOPPED=_WIFSTOPPED, - _WSTOPSIG=_WSTOPSIG): + def _handle_exitstatus(self, sts, _del_safe=_del_safe): """All callers to this function MUST hold self._waitpid_lock.""" # This method is called (indirectly) by __del__, so it cannot # refer to anything outside of its local scope. - if _WIFSTOPPED(sts): - self.returncode = -_WSTOPSIG(sts) + if _del_safe.WIFSTOPPED(sts): + self.returncode = -_del_safe.WSTOPSIG(sts) else: - self.returncode = _waitstatus_to_exitcode(sts) + self.returncode = _del_safe.waitstatus_to_exitcode(sts) - def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, - _WNOHANG=_WNOHANG, _ECHILD=errno.ECHILD): + def _internal_poll(self, _deadstate=None, _del_safe=_del_safe): """Check if child process has terminated. Returns returncode attribute. @@ -1987,13 +1987,13 @@ def _internal_poll(self, _deadstate=None, _waitpid=_waitpid, try: if self.returncode is not None: return self.returncode # Another thread waited. - pid, sts = _waitpid(self.pid, _WNOHANG) + pid, sts = _del_safe.waitpid(self.pid, _del_safe.WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except OSError as e: if _deadstate is not None: self.returncode = _deadstate - elif e.errno == _ECHILD: + elif e.errno == _del_safe.ECHILD: # This happens if SIGCLD is set to be ignored or # waiting for child processes has otherwise been # disabled for our process. This child is dead, we diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index 122d441bd1..fdf2f4e471 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -96,6 +96,33 @@ 'scripts': '{base}/Scripts', 'data': '{base}', }, + 'ios': { + 'stdlib': '{installed_base}/lib/python{py_version_short}', + 'platstdlib': '{installed_base}/lib/python{py_version_short}', + 'purelib': '{installed_base}/lib/python{py_version_short}/site-packages', + 'platlib': '{installed_base}/lib/python{py_version_short}/site-packages', + 'include': '{installed_base}/include', + 'scripts': '{installed_base}/bin', + 'data': '{installed_base}/Resources', + }, + 'tvos': { + 'stdlib': '{installed_base}/lib/python{py_version_short}', + 'platstdlib': '{installed_base}/lib/python{py_version_short}', + 'purelib': '{installed_base}/lib/python{py_version_short}/site-packages', + 'platlib': '{installed_base}/lib/python{py_version_short}/site-packages', + 'include': '{installed_base}/include', + 'scripts': '{installed_base}/bin', + 'data': '{installed_base}/Resources', + }, + 'watchos': { + 'stdlib': '{installed_base}/lib/python{py_version_short}', + 'platstdlib': '{installed_base}/lib/python{py_version_short}', + 'purelib': '{installed_base}/lib/python{py_version_short}/site-packages', + 'platlib': '{installed_base}/lib/python{py_version_short}/site-packages', + 'include': '{installed_base}/include', + 'scripts': '{installed_base}/bin', + 'data': '{installed_base}/Resources', + }, } # For the OS-native venv scheme, we essentially provide an alias: @@ -288,12 +315,19 @@ def _get_preferred_schemes(): 'home': 'posix_home', 'user': 'nt_user', } + if sys.platform in ('ios', 'tvos', 'watchos'): + return { + 'prefix': sys.platform, + 'home': sys.platform, + 'user': sys.platform, + } if sys.platform == 'darwin' and sys._framework: return { 'prefix': 'posix_prefix', 'home': 'posix_home', 'user': 'osx_framework_user', } + return { 'prefix': 'posix_prefix', 'home': 'posix_home', @@ -813,10 +847,16 @@ def get_platform(): if m: release = m.group() elif osname[:6] == "darwin": - import _osx_support - osname, release, machine = _osx_support.get_platform_osx( - get_config_vars(), - osname, release, machine) + if sys.platform in ("ios", "tvos", "watchos"): + import _ios_support + _, release, _ = _ios_support.get_platform_ios() + osname = sys.platform + machine = sys.implementation._multiarch + else: + import _osx_support + osname, release, machine = _osx_support.get_platform_osx( + get_config_vars(), + osname, release, machine) return f"{osname}-{release}-{machine}" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 4e793f1549..9d000ab926 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -42,7 +42,7 @@ "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", "requires_limited_api", "requires_specialization", # sys - "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", + "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", "is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval", # os "get_pagesize", @@ -530,7 +530,7 @@ def requires_legacy_unicode_capi(): is_android = hasattr(sys, 'getandroidapilevel') -if sys.platform not in ('win32', 'vxworks'): +if sys.platform not in ('win32', 'vxworks', 'ios', 'tvos', 'watchos'): unix_shell = '/system/bin/sh' if is_android else '/bin/sh' else: unix_shell = None @@ -540,12 +540,25 @@ def requires_legacy_unicode_capi(): is_emscripten = sys.platform == "emscripten" is_wasi = sys.platform == "wasi" -has_fork_support = hasattr(os, "fork") and not is_emscripten and not is_wasi +# Apple mobile platforms (iOS/tvOS/watchOS) are POSIX-like but do not +# have subprocess or fork support. +is_apple_mobile = sys.platform in ('ios', 'tvos', 'watchos') + +has_fork_support = ( + hasattr(os, "fork") + and not is_emscripten + and not is_wasi + and not is_apple_mobile +) def requires_fork(): return unittest.skipUnless(has_fork_support, "requires working os.fork()") -has_subprocess_support = not is_emscripten and not is_wasi +has_subprocess_support = ( + not is_emscripten + and not is_wasi + and not is_apple_mobile +) def requires_subprocess(): """Used for subprocess, os.spawn calls, fd inheritance""" diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index f25580371a..e8793aaec5 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -34,6 +34,7 @@ from multiprocessing.util import _cleanup_tests as multiprocessing_cleanup_tests from test.test_asyncio import utils as test_utils from test import support +from test.support import is_apple_mobile from test.support import socket_helper from test.support import threading_helper from test.support import ALWAYS_EQ, LARGEST, SMALLEST @@ -544,6 +545,7 @@ def test_create_connection(self): self._basetest_create_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. @@ -636,6 +638,7 @@ def _dummy_ssl_create_context(purpose=ssl.Purpose.SERVER_AUTH, *, self.assertEqual(cm.exception.reason, 'CERTIFICATE_VERIFY_FAILED') @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_connection(self): with test_utils.run_test_server(use_ssl=True) as httpd: create_connection = functools.partial( @@ -647,6 +650,7 @@ def test_create_ssl_connection(self): @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_ssl_unix_connection(self): # Issue #20682: On Mac OS X Tiger, getsockname() returns a # zero-length address for UNIX socket. @@ -928,6 +932,7 @@ def _make_unix_server(self, factory, **kwargs): return server, path @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server(self): proto = MyProto(loop=self.loop) server, path = self._make_unix_server(lambda: proto) @@ -956,6 +961,7 @@ def test_create_unix_server(self): server.close() @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_path_socket_error(self): proto = MyProto(loop=self.loop) sock = socket.socket() @@ -1021,6 +1027,7 @@ def test_create_server_ssl(self): @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( @@ -1051,6 +1058,7 @@ def test_create_unix_server_ssl(self): server.close() @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, host, port = self._make_ssl_server( @@ -1081,6 +1089,7 @@ def test_create_server_ssl_verify_failed(self): @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( @@ -1145,6 +1154,7 @@ def test_create_server_ssl_match_failed(self): @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_create_unix_server_ssl_verified(self): proto = MyProto(loop=self.loop) server, path = self._make_ssl_unix_server( diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 3c8cc5f364..d2b6e0f636 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -18,6 +18,7 @@ import asyncio from test.test_asyncio import utils as test_utils +from test.support import is_apple_mobile def tearDownModule(): @@ -60,6 +61,7 @@ def test_open_connection(self): self._basetest_open_connection(conn_fut) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address) @@ -91,6 +93,7 @@ def test_open_connection_no_loop_ssl(self): @socket_helper.skip_unless_bind_unix_socket @unittest.skipIf(ssl is None, 'No ssl module') + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_no_loop_ssl(self): with test_utils.run_test_unix_server(use_ssl=True) as httpd: conn_fut = asyncio.open_unix_connection( @@ -119,6 +122,7 @@ def test_open_connection_error(self): self._basetest_open_connection_error(conn_fut) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_open_unix_connection_error(self): with test_utils.run_test_unix_server() as httpd: conn_fut = asyncio.open_unix_connection(httpd.address) @@ -637,6 +641,7 @@ async def client(addr): self.assertEqual(messages, []) @socket_helper.skip_unless_bind_unix_socket + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_start_unix_server(self): class MyServer: diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index d2c8cba6ac..a7bbe1d2b0 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -18,6 +18,7 @@ import warnings from test import support +from test.support import is_apple_mobile from test.support import os_helper from test.support import socket_helper from test.support import wait_process @@ -283,6 +284,7 @@ def test_close_on_finalizing(self, m_signal, m_sys): @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'UNIX Sockets are not supported') +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class SelectorEventLoopUnixSocketTests(test_utils.TestCase): def setUp(self): diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 203dd6fe57..8e0999ecd7 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -6,7 +6,7 @@ import struct import sys import unittest -from test.support import verbose, cpython_only, get_pagesize +from test.support import cpython_only, get_pagesize, is_apple_mobile, verbose from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink @@ -57,7 +57,7 @@ def get_lockdata(): start_len = "qq" if (sys.platform.startswith(('netbsd', 'freebsd', 'openbsd')) - or sys.platform == 'darwin'): + or sys.platform == 'darwin' or is_apple_mobile): if struct.calcsize('l') == 8: off_t = 'l' pid_t = 'i' diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 15f944734c..0a597bc6bc 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -31,6 +31,7 @@ import unittest from test import support +from test.support import is_apple_mobile from test.support import os_helper from test.support import threading_helper @@ -423,7 +424,7 @@ def test_undecodable_filename(self): with open(os.path.join(self.tempdir, filename), 'wb') as f: f.write(os_helper.TESTFN_UNDECODABLE) response = self.request(self.base_url + '/') - if sys.platform == 'darwin': + if sys.platform == 'darwin' or is_apple_mobile: # On Mac OS the HFS+ filesystem replaces bytes that aren't valid # UTF-8 into a percent-encoded value. for name in os.listdir(self.tempdir): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index daa40a6ba3..526f6c04a2 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -40,6 +40,7 @@ from test.support.script_helper import ( assert_python_ok, assert_python_failure, run_python_until_end) from test.support import import_helper +from test.support import is_apple_mobile from test.support import os_helper from test.support import threading_helper from test.support import warnings_helper @@ -634,7 +635,7 @@ def test_large_file_ops(self): # On Windows and Mac OSX this test consumes large resources; It takes # a long time to build the >2 GiB file and takes >2 GiB of disk space # therefore the resource must be enabled to run this test. - if sys.platform[:3] == 'win' or sys.platform == 'darwin': + if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: support.requires( 'largefile', 'test requires %s bytes and a long time to run' % self.LARGE) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index a4b2b4f9c8..8587ff2615 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -44,6 +44,7 @@ from test.support.script_helper import assert_python_ok, assert_python_failure from test import support from test.support import import_helper +from test.support import is_apple_mobile from test.support import os_helper from test.support import socket_helper from test.support import threading_helper @@ -1942,6 +1943,7 @@ def test_noserver(self): @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSocketHandlerTest(SocketHandlerTest): """Test for SocketHandler with unix sockets.""" @@ -2022,6 +2024,7 @@ def test_output(self): self.assertEqual(self.log_output, "spam\neggs\n") @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixDatagramHandlerTest(DatagramHandlerTest): """Test for DatagramHandler using Unix sockets.""" @@ -2113,6 +2116,7 @@ def test_udp_reconnection(self): self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00') @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) class UnixSysLogHandlerTest(SysLogHandlerTest): """Test for SysLogHandler with Unix sockets.""" diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py index 3d9d6d5d0a..dfb1d6f84d 100644 --- a/Lib/test/test_marshal.py +++ b/Lib/test/test_marshal.py @@ -1,5 +1,5 @@ from test import support -from test.support import os_helper, requires_debug_ranges +from test.support import os_helper, requires_debug_ranges, is_apple_mobile from test.support.script_helper import assert_python_ok import array import io @@ -263,7 +263,10 @@ def test_recursion_limit(self): elif sys.platform == 'wasi': MAX_MARSHAL_STACK_DEPTH = 1500 else: - MAX_MARSHAL_STACK_DEPTH = 2000 + if is_apple_mobile: + MAX_MARSHAL_STACK_DEPTH = 1500 + else: + MAX_MARSHAL_STACK_DEPTH = 2000 for i in range(MAX_MARSHAL_STACK_DEPTH - 2): last.append([0]) last = last[-1] diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 1867e8c957..71afa06843 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -1,5 +1,5 @@ from test.support import ( - requires, _2G, _4G, gc_collect, cpython_only, is_emscripten + requires, _2G, _4G, gc_collect, cpython_only, is_emscripten, is_apple_mobile ) from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink @@ -245,7 +245,7 @@ def test_access_parameter(self): with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) - if os.name == "posix": + if os.name == "posix" and not is_apple_mobile: # Try incompatible flags, prot and access parameters. with open(TESTFN, "r+b") as f: self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, @@ -1009,7 +1009,7 @@ def tearDown(self): unlink(TESTFN) def _make_test_file(self, num_zeroes, tail): - if sys.platform[:3] == 'win' or sys.platform == 'darwin': + if sys.platform[:3] == 'win' or sys.platform == 'darwin' or is_apple_mobile: requires('largefile', 'test requires %s bytes and a long time to run' % str(0x180000000)) f = open(TESTFN, 'w+b') diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index b62a9e3897..585a93df73 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -8,7 +8,7 @@ from unittest import mock from test import support -from test.support import os_helper +from test.support import os_helper, is_apple_mobile FEDORA_OS_RELEASE = """\ NAME=Fedora @@ -356,7 +356,7 @@ def test_win32_ver_on_non_windows(self): def test_mac_ver(self): res = platform.mac_ver() - if platform.uname().system == 'Darwin': + if platform.uname().system == 'Darwin' and not is_apple_mobile: # We are on a macOS system, check that the right version # information is returned output = subprocess.check_output(['sw_vers'], text=True) @@ -388,6 +388,9 @@ def test_mac_ver(self): else: self.assertEqual(res[2], 'PowerPC') + @unittest.skipUnless(is_apple_mobile, 'iOS/tvOS/watchOS only test') + def test_ios_ver(self): + res = platform.ios_ver() @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") def test_mac_ver_with_fork(self): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index f115aa874f..a89b260bfd 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -2,6 +2,7 @@ from test import support from test.support import import_helper +from test.support import is_apple_mobile from test.support import os_helper from test.support import warnings_helper from test.support.script_helper import assert_python_ok @@ -71,12 +72,19 @@ def testNoArgFunctions(self): "getpid", "getpgrp", "getppid", "getuid", "sync", ] + # getgroups can't be invoked on iOS/tvOS/watchOS. + if is_apple_mobile: + NO_ARG_FUNCTIONS.append("getgroups") + for name in NO_ARG_FUNCTIONS: posix_func = getattr(posix, name, None) if posix_func is not None: with self.subTest(name): - posix_func() - self.assertRaises(TypeError, posix_func, 1) + try: + posix_func() + self.assertRaises(TypeError, posix_func, 1) + except Exception as e: + self.fail('Problem invoking %s: %s' % (name, e)) @unittest.skipUnless(hasattr(posix, 'getresuid'), 'test needs posix.getresuid()') @@ -781,9 +789,10 @@ def check_stat(uid, gid): check_stat(uid, gid) self.assertRaises(OSError, chown_func, first_param, 0, -1) check_stat(uid, gid) - if 0 not in os.getgroups(): - self.assertRaises(OSError, chown_func, first_param, -1, 0) - check_stat(uid, gid) + if hasattr(os, 'getgroups') and not is_apple_mobile: + if 0 not in os.getgroups(): + self.assertRaises(OSError, chown_func, first_param, -1, 0) + check_stat(uid, gid) # test illegal types for t in str, float: self.assertRaises(TypeError, chown_func, first_param, t(uid), gid) @@ -1250,7 +1259,7 @@ def test_sched_priority(self): self.assertIsInstance(hi, int) self.assertGreaterEqual(hi, lo) # OSX evidently just returns 15 without checking the argument. - if sys.platform != "darwin": + if sys.platform != 'darwin' and not is_apple_mobile: self.assertRaises(OSError, posix.sched_get_priority_min, -23) self.assertRaises(OSError, posix.sched_get_priority_max, -23) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 49fcd78fd2..5dfb8ce14e 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -2113,6 +2113,7 @@ def check_chown(path, uid=None, gid=None): check_chown(dirname, uid, gid) +@unittest.skipIf(support.has_subprocess_support, 'Test requires support for subprocesses.') class TestWhich(BaseTest, unittest.TestCase): def setUp(self): @@ -3146,6 +3147,7 @@ def test_bad_environ(self): self.assertGreaterEqual(size.lines, 0) @unittest.skipUnless(os.isatty(sys.__stdout__.fileno()), "not on tty") + @unittest.skipUnless(support.has_subprocess_support, 'Test requires support for subprocesses.') @unittest.skipUnless(hasattr(os, 'get_terminal_size'), 'need os.get_terminal_size()') def test_stty_match(self): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 37f7fd5a04..ce2ca007e3 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1,5 +1,6 @@ import unittest from test import support +from test.support import is_apple_mobile from test.support import os_helper from test.support import socket_helper from test.support import threading_helper @@ -1170,7 +1171,7 @@ def testGetServBy(self): # I've ordered this by protocols that have both a tcp and udp # protocol, at least for modern Linuxes. if (sys.platform.startswith(('freebsd', 'netbsd', 'gnukfreebsd')) - or sys.platform in ('linux', 'darwin')): + or sys.platform in ('linux', 'darwin') or is_apple_mobile): # avoid the 'echo' service on this platform, as there is an # assumption breaking non-standard port/protocol entry services = ('daytime', 'qotd', 'domain') @@ -3681,7 +3682,7 @@ def testFDPassCMSG_LEN(self): def _testFDPassCMSG_LEN(self): self.createAndSendFDs(1) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparate(self): @@ -3692,7 +3693,7 @@ def testFDPassSeparate(self): maxcmsgs=2) @testFDPassSeparate.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparate(self): fd0, fd1 = self.newFDs(2) @@ -3705,7 +3706,7 @@ def _testFDPassSeparate(self): array.array("i", [fd1]))]), len(MSG)) - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") @requireAttrs(socket, "CMSG_SPACE") def testFDPassSeparateMinSpace(self): @@ -3719,7 +3720,7 @@ def testFDPassSeparateMinSpace(self): maxcmsgs=2, ignoreflags=socket.MSG_CTRUNC) @testFDPassSeparateMinSpace.client_skip - @unittest.skipIf(sys.platform == "darwin", "skipping, see issue #12958") + @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") @unittest.skipIf(AIX, "skipping, see issue #22397") def _testFDPassSeparateMinSpace(self): fd0, fd1 = self.newFDs(2) @@ -3743,7 +3744,7 @@ def sendAncillaryIfPossible(self, msg, ancdata): nbytes = self.sendmsgToServer([msg]) self.assertEqual(nbytes, len(msg)) - @unittest.skipIf(sys.platform == "darwin", "see issue #24725") + @unittest.skipIf(sys.platform == "darwin" or is_apple_mobile, "skipping, see issue #12958") def testFDPassEmpty(self): # Try to pass an empty FD array. Can receive either no array # or an empty array. @@ -4563,28 +4564,33 @@ class SendrecvmsgUnixStreamTestBase(SendrecvmsgConnectedBase, pass @requireAttrs(socket.socket, "sendmsg") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class SendmsgUnixStreamTest(SendmsgStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgUnixStreamTest(RecvmsgTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "recvmsg_into") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX") class RecvmsgIntoUnixStreamTest(RecvmsgIntoTests, RecvmsgGenericStreamTests, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgSCMRightsStreamTest(SCMRightsTest, SendrecvmsgUnixStreamTestBase): pass @requireAttrs(socket.socket, "sendmsg", "recvmsg_into") +@unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) @requireAttrs(socket, "AF_UNIX", "SOL_SOCKET", "SCM_RIGHTS") class RecvmsgIntoSCMRightsStreamTest(RecvmsgIntoMixin, SCMRightsTest, SendrecvmsgUnixStreamTestBase): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 0f62f9eb20..f7a8a82e1d 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -8,12 +8,13 @@ import select import signal import socket +import sys import threading import unittest import socketserver import test.support -from test.support import reap_children, verbose +from test.support import is_apple_mobile, reap_children, verbose from test.support import os_helper from test.support import socket_helper from test.support import threading_helper @@ -181,12 +182,14 @@ def test_ForkingTCPServer(self): self.stream_examine) @requires_unix_sockets + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_UnixStreamServer(self): self.run_server(socketserver.UnixStreamServer, socketserver.StreamRequestHandler, self.stream_examine) @requires_unix_sockets + @unittest.skipIf(is_apple_mobile, "%s doesn't fully support UNIX sockets." % sys.platform) def test_ThreadingUnixStreamServer(self): self.run_server(socketserver.ThreadingUnixStreamServer, socketserver.StreamRequestHandler, diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 1137c2032b..da715f9c39 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -344,7 +344,7 @@ def test_get_config_h_filename(self): self.assertTrue(os.path.isfile(config_h), config_h) def test_get_scheme_names(self): - wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv'] + wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv', 'tvos', 'watchos'] if HAS_USER_BASE: wanted.extend(['nt_user', 'osx_framework_user', 'posix_user']) self.assertEqual(get_scheme_names(), tuple(sorted(wanted))) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 2e4b860b97..47f27d19f7 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -3,7 +3,7 @@ """ import test.support -from test.support import threading_helper, requires_subprocess +from test.support import threading_helper, requires_subprocess, is_apple_mobile from test.support import verbose, cpython_only, os_helper from test.support.import_helper import import_module from test.support.script_helper import assert_python_ok, assert_python_failure @@ -1364,6 +1364,7 @@ def pipe(self): os.set_blocking(r, False) return (r, w) + @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join(self): # Non-daemon threads should be joined at subinterpreter shutdown # (issue #18808) @@ -1392,6 +1393,7 @@ def f(): # The thread was joined properly. self.assertEqual(os.read(r, 1), b"x") + @unittest.skipIf(is_apple_mobile, "%s doesn't have os.pipe" % sys.platform) def test_threads_join_2(self): # Same as above, but a delay gets introduced after the thread's # Python code returned but before the thread state is deleted. diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 2f4417621e..bee49d95d0 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -19,7 +19,7 @@ import tempfile from test.support import (captured_stdout, captured_stderr, skip_if_broken_multiprocessing_synchronize, verbose, - requires_subprocess, is_emscripten, is_wasi, + requires_subprocess, is_apple_mobile, is_emscripten, is_wasi, requires_venv_with_pip, TEST_HOME_DIR, requires_resource, copy_python_src_ignore) from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) @@ -41,6 +41,8 @@ if is_emscripten or is_wasi: raise unittest.SkipTest("venv is not available on Emscripten/WASI.") +if is_apple_mobile: + raise unittest.SkipTest("venv is not available on mobile Apple platforms.") @requires_subprocess() def check_output(cmd, encoding=None): diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index ba6711e4ef..3d8d6cfa52 100755 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -542,6 +542,57 @@ def register_standard_browsers(): # what to do if _tryorder is now empty? +# +# Platform support for iOS +# +if sys.platform == 'ios': + class MobileSafari(BaseBrowser): + def open(self, url, new=0, autoraise=True): + # This code is the equivalent of: + # NSURL *nsurl = [NSURL URLWithString:url]; + # [[UIApplication sharedApplication] openURL:nsurl]; + from ctypes import cdll, c_void_p, c_char_p, c_uint32 + from ctypes import util + objc = cdll.LoadLibrary(util.find_library(b'objc')) + cf = cdll.LoadLibrary(util.find_library(b'CoreFoundation')) + objc.objc_getClass.restype = c_void_p + objc.objc_getClass.argtypes = [c_char_p] + objc.sel_registerName.restype = c_void_p + objc.sel_registerName.argtypes = [c_char_p] + cf.CFStringCreateWithCString.restype = c_void_p + cf.CFStringCreateWithCString.argtypes = [c_void_p, c_char_p, c_uint32] + + # Get an NSString describing the URL + kCFStringEncodingUTF8 = 0x08000100 + url = c_void_p(cf.CFStringCreateWithCString(None, url.encode('utf-8'), kCFStringEncodingUTF8)) + autorelease = c_void_p(objc.sel_registerName(b'autorelease')) + objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + objc.objc_msgSend.restype = c_void_p + objc.objc_msgSend(url, autorelease) + + # Get an NSURL object representing the URL + NSURL = c_void_p(objc.objc_getClass(b'NSURL')) + urlWithString_ = c_void_p(objc.sel_registerName(b'URLWithString:')) + objc.objc_msgSend.restype = c_void_p + objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p] + nsurl = c_void_p(objc.objc_msgSend(NSURL, urlWithString_, url)) + + # Get the shared UIApplication instance + UIApplication = c_void_p(objc.objc_getClass(b'UIApplication')) + sharedApplication = c_void_p(objc.sel_registerName(b'sharedApplication')) + objc.objc_msgSend.argtypes = [c_void_p, c_void_p] + objc.objc_msgSend.restype = c_void_p + shared_app = c_void_p(objc.objc_msgSend(UIApplication, sharedApplication)) + + # Open the URL on the shared application + openURL_ = c_void_p(objc.sel_registerName(b'openURL:')) + objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p] + objc.objc_msgSend.restype = None + objc.objc_msgSend(shared_app, openURL_, nsurl) + + return True + + register("mobilesafari", None, MobileSafari(), preferred=True) # # Platform support for Windows diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 35ea2ac306..b3b94bf49f 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -32,10 +32,20 @@ #include "posixmodule.h" +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + #ifdef _Py_MEMORY_SANITIZER # include #endif +// iOS/tvOS/watchOS *define* a number of POSIX functions, but you can't use them +// because they aren't conventional multiprocess environments. +#if TARGET_OS_IPHONE +# undef HAVE_FORK +#endif + #if defined(__ANDROID__) && __ANDROID_API__ < 21 && !defined(SYS_getdents64) # include # define SYS_getdents64 __NR_getdents64 @@ -732,11 +742,16 @@ child_exec(char *const exec_array[], saved_errno = 0; for (i = 0; exec_array[i] != NULL; ++i) { const char *executable = exec_array[i]; + +#if TARGET_OS_TV || TARGET_OS_WATCH + errno = ENOTSUP; +#else if (envp) { execve(executable, argv, envp); } else { execv(executable, argv); } +#endif /* TARGET_OS_TV || TARGET_OS_WATCH */ if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) { saved_errno = errno; } @@ -798,7 +813,9 @@ do_fork_exec(char *const exec_array[], PyObject *preexec_fn, PyObject *preexec_fn_args_tuple) { - +/* iOS/tvOS/watchOS define the existence of fork, but it cannot be invoked; + * so fail fast if any attempt is made to invoke fork_exec */ +#ifdef HAVE_FORK pid_t pid; #ifdef VFORK_USABLE @@ -833,7 +850,7 @@ do_fork_exec(char *const exec_array[], pid = fork(); } } else -#endif +#endif /* VFORK_USABLE */ { pid = fork(); } @@ -866,6 +883,9 @@ do_fork_exec(char *const exec_array[], preexec_fn, preexec_fn_args_tuple); _exit(255); return 0; /* Dead code to avoid a potential compiler warning. */ +#else /* HAVE_FORK */ + return -1; +#endif /* HAVE_FORK */ } /*[clinic input] @@ -945,6 +965,10 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, int *c_fds_to_keep = NULL; Py_ssize_t fds_to_keep_len = PyTuple_GET_SIZE(py_fds_to_keep); +/* iOS/tvOS/watchOS define the existence of fork, but it cannot be invoked; + * so fail fast if any attempt is made to invoke fork_exec */ +#ifdef HAVE_FORK + PyInterpreterState *interp = PyInterpreterState_Get(); if ((preexec_fn != Py_None) && _PyInterpreterState_GetFinalizing(interp) != NULL) @@ -1151,7 +1175,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, } old_sigmask = &old_sigs; } -#endif +#endif /* VFORK_USABLE */ pid = do_fork_exec(exec_array, argv, envp, cwd, p2cread, p2cwrite, c2pread, c2pwrite, @@ -1184,7 +1208,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, * the thread signal mask. */ (void) pthread_sigmask(SIG_SETMASK, old_sigmask, NULL); } -#endif +#endif /* VFORK_USABLE */ if (need_after_fork) PyOS_AfterFork_Parent(); @@ -1218,6 +1242,10 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, } return pid == -1 ? NULL : PyLong_FromPid(pid); + +#else /* HAVE_FORK */ + return NULL; +#endif } /* module level code ********************************************************/ diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 23fa2b1816..587ad7ace9 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -198,6 +198,10 @@ tl_to_d(TripleLength total) } +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + /* sin(pi*x), giving accurate results for all finite x (especially x integral or close to an integer). This is here for use in the diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 8fce40bb68..392d88cdf7 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -78,6 +78,8 @@ #include +#include "TargetConditionals.h" + #if defined(__has_builtin) #if __has_builtin(__builtin_available) #define HAVE_BUILTIN_AVAILABLE 1 @@ -364,6 +366,26 @@ corresponding Unix manual entries for more information on calls."); # define fsync _commit #endif /* ! __WATCOMC__ || __QNX__ */ +// iOS/tvOS/watchOS *define* a number of POSIX functions, but you can't use them +// because they aren't conventional multiprocess environment. +#if TARGET_OS_IPHONE +# undef HAVE_EXECV +# undef HAVE_FORK +# undef HAVE_FORK1 +# undef HAVE_FORKPTY +# undef HAVE_GETGROUPS +# undef HAVE_POSIX_SPAWN +# undef HAVE_POSIX_SPAWNP +# undef HAVE_SCHED_H +# undef HAVE_SENDFILE +# undef HAVE_SETPRIORITY +# undef HAVE_SPAWNV +# undef HAVE_WAIT +# undef HAVE_WAIT3 +# undef HAVE_WAIT4 +# undef HAVE_WAITPID +#endif + /*[clinic input] # one of the few times we lie about this name! module os @@ -1538,7 +1560,9 @@ _Py_Sigset_Converter(PyObject *obj, void *addr) */ #include #elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) +# if !TARGET_OS_TV && !TARGET_OS_WATCH extern char **environ; +# endif #endif /* !_MSC_VER */ static PyObject * @@ -1554,6 +1578,7 @@ convertenviron(void) d = PyDict_New(); if (d == NULL) return NULL; +#if !TARGET_OS_TV && !TARGET_OS_WATCH #ifdef MS_WINDOWS /* _wenviron must be initialized in this way if the program is started through main() instead of wmain(). */ @@ -1607,6 +1632,7 @@ convertenviron(void) Py_DECREF(k); Py_DECREF(v); } +#endif /* !TARGET_OS_TV && !TARGET_OS_WATCH */ return d; } @@ -5713,6 +5739,9 @@ os_system_impl(PyObject *module, PyObject *command) /*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ { long result; +#if TARGET_OS_IPHONE + result = -1; +#else const char *bytes = PyBytes_AsString(command); if (PySys_Audit("os.system", "(O)", command) < 0) { @@ -5722,6 +5751,7 @@ os_system_impl(PyObject *module, PyObject *command) Py_BEGIN_ALLOW_THREADS result = system(bytes); Py_END_ALLOW_THREADS +#endif /* TARGET_OS_IPHONE */ return result; } #endif @@ -14767,6 +14797,7 @@ DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, int is_symlink; int need_stat; #endif +#if !TARGET_OS_TV && !TARGET_OS_WATCH #ifdef MS_WINDOWS unsigned long dir_bits; #endif @@ -14827,6 +14858,7 @@ DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, #endif return result; +#endif /* !TARGET_OS_TV && !TARGET_OS_WATCH */ error: Py_XDECREF(st_mode); diff --git a/Modules/pwdmodule.c b/Modules/pwdmodule.c index 920259a62c..fff2f1771b 100644 --- a/Modules/pwdmodule.c +++ b/Modules/pwdmodule.c @@ -1,6 +1,10 @@ /* UNIX password file access module */ +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + #include "Python.h" #include "posixmodule.h" @@ -181,6 +185,22 @@ pwd_getpwuid(PyObject *module, PyObject *uidobj) if (nomem == 1) { return PyErr_NoMemory(); } + +// iPhone has a "user" with UID 501, username "mobile"; but the simulator +// doesn't reflect this. Generate a simulated response. +#if TARGET_IPHONE_SIMULATOR + if (uid == 501) { + struct passwd mp; + mp.pw_name = "mobile"; + mp.pw_passwd = "/smx7MYTQIi2M"; + mp.pw_uid = 501; + mp.pw_gid = 501; + mp.pw_gecos = "Mobile User"; + mp.pw_dir = "/var/mobile"; + mp.pw_shell = "/bin/sh"; + return mkpwent(module, &mp); + } +#endif PyObject *uid_obj = _PyLong_FromUid(uid); if (uid_obj == NULL) return NULL; @@ -264,6 +284,22 @@ pwd_getpwnam_impl(PyObject *module, PyObject *name) PyErr_NoMemory(); } else { +// iPhone has a "user" with UID 501, username "mobile"; but the simulator +// doesn't reflect this. Generate a simulated response. +#if TARGET_IPHONE_SIMULATOR + if (strcmp(name, "mobile") == 0) { + struct passwd mp; + mp.pw_name = "mobile"; + mp.pw_passwd = "/smx7MYTQIi2M"; + mp.pw_uid = 501; + mp.pw_gid = 501; + mp.pw_gecos = "Mobile User"; + mp.pw_dir = "/var/mobile"; + mp.pw_shell = "/bin/sh"; + retval = mkpwent(module, &mp); + goto out; + } +#endif PyErr_Format(PyExc_KeyError, "getpwnam(): name not found: %R", name); } diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 3b46deacdf..eb34ed6844 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -114,6 +114,11 @@ _PyTime_Init(void) } +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + + /* Forward declarations */ static int pysleep(_PyTime_t timeout); @@ -305,11 +310,13 @@ time_clock_settime(PyObject *self, PyObject *args) if (_PyTime_AsTimespec(t, &tp) == -1) return NULL; +#if !TARGET_OS_IPHONE ret = clock_settime((clockid_t)clk_id, &tp); if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } +#endif Py_RETURN_NONE; } @@ -338,11 +345,13 @@ time_clock_settime_ns(PyObject *self, PyObject *args) return NULL; } +#if !TARGET_OS_IPHONE ret = clock_settime((clockid_t)clk_id, &ts); if (ret != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } +#endif Py_RETURN_NONE; } diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c index 587063ef1a..94f2dd3d70 100644 --- a/Python/bootstrap_hash.c +++ b/Python/bootstrap_hash.c @@ -36,6 +36,10 @@ #endif +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + #ifdef Py_DEBUG int _Py_HashSecret_Initialized = 0; #else @@ -181,6 +185,9 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise) } #elif defined(HAVE_GETENTROPY) +// iOS, tvOS and watchOS have an incomplete definitions of getentropy +// so it is *found* by configure, but doesn't actually exist. +#elif defined(HAVE_GETENTROPY) && !TARGET_OS_IPHONE #define PY_GETENTROPY 1 /* Fill buffer with size pseudo-random bytes generated by getentropy(): diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index 6761bba457..0ba0005741 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -28,6 +28,10 @@ #define LEAD_UNDERSCORE "" #endif +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + /* The .so extension module ABI tag, supplied by the Makefile via Makefile.pre.in and configure. This is used to discriminate between incompatible .so files so that extensions for different Python builds can @@ -38,12 +42,15 @@ const char *_PyImport_DynLoadFiletab[] = { #ifdef __CYGWIN__ ".dll", #else /* !__CYGWIN__ */ - "." SOABI ".so", -#ifdef ALT_SOABI - "." ALT_SOABI ".so", -#endif - ".abi" PYTHON_ABI_STRING ".so", - ".so", +# ifdef __APPLE__ +# define SHLIB_SUFFIX ".so" +# endif + "." SOABI SHLIB_SUFFIX, +# ifdef ALT_SOABI + "." ALT_SOABI SHLIB_SUFFIX, +# endif + ".abi" PYTHON_ABI_STRING SHLIB_SUFFIX, + SHLIB_SUFFIX, #endif /* __CYGWIN__ */ NULL, }; diff --git a/Python/marshal.c b/Python/marshal.c index 90953cbb72..36e38f3fc6 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -15,6 +15,10 @@ #include "pycore_hashtable.h" // _Py_hashtable_t #include "marshal.h" // Py_MARSHAL_VERSION +#ifdef __APPLE__ +# include "TargetConditionals.h" +#endif /* __APPLE__ */ + /*[clinic input] module marshal [clinic start generated code]*/ @@ -34,11 +38,15 @@ module marshal * #if defined(MS_WINDOWS) && defined(_DEBUG) */ #if defined(MS_WINDOWS) -#define MAX_MARSHAL_STACK_DEPTH 1000 +# define MAX_MARSHAL_STACK_DEPTH 1000 #elif defined(__wasi__) -#define MAX_MARSHAL_STACK_DEPTH 1500 +# define MAX_MARSHAL_STACK_DEPTH 1500 #else -#define MAX_MARSHAL_STACK_DEPTH 2000 +# if TARGET_OS_IPHONE +# define MAX_MARSHAL_STACK_DEPTH 1500 +# else +# define MAX_MARSHAL_STACK_DEPTH 2000 +# endif #endif #define TYPE_NULL '0' diff --git a/Python/sysmodule.c b/Python/sysmodule.c index a99a97fef0..1767e25021 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -48,6 +48,10 @@ extern void *PyWin_DLLhModule; extern const char *PyWin_DLLVersionString; #endif +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + #ifdef __EMSCRIPTEN__ #include #endif @@ -3117,6 +3121,15 @@ make_impl_info(PyObject *version_info) goto error; #endif +#if TARGET_OS_IPHONE +# if TARGET_OS_SIMULATOR + res = PyDict_SetItemString(impl_info, "_simulator", Py_True); +# else + res = PyDict_SetItemString(impl_info, "_simulator", Py_False); +# endif + if (res < 0) + goto error; +#endif /* dict ready */ ns = _PyNamespace_New(impl_info); diff --git a/config.sub b/config.sub index d74fb6deac..a30f74f03d 100755 --- a/config.sub +++ b/config.sub @@ -1121,10 +1121,9 @@ case $cpu-$vendor in xscale-* | xscalee[bl]-*) cpu=`echo "$cpu" | sed 's/^xscale/arm/'` ;; - arm64-*) + arm64-* | arm64_32-*) cpu=aarch64 ;; - # Recognize the canonical CPU Types that limit and/or modify the # company names they are paired with. cr16-*) @@ -1723,7 +1722,7 @@ case $os in | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | hiux* | abug | nacl* | netware* | windows* \ - | os9* | macos* | osx* | ios* \ + | os9* | macos* | osx* | ios* | tvos* | watchos* \ | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ @@ -1786,6 +1785,8 @@ case $kernel-$os in ;; *-eabi* | *-gnueabi*) ;; + ios*-simulator | tvos*-simulator | watchos*-simulator) + ;; -*) # Blank kernel with real OS is always fine. ;; diff --git a/configure b/configure index be783faa24..f3cc3dae13 100755 --- a/configure +++ b/configure @@ -4270,6 +4270,15 @@ then *-*-cygwin*) ac_sys_system=Cygwin ;; + *-apple-ios*) + ac_sys_system=iOS + ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -4326,27 +4335,96 @@ if test "$cross_compiling" = yes; then *-*-linux*) case "$host_cpu" in arm*) - _host_cpu=arm + _host_ident=arm ;; *) - _host_cpu=$host_cpu + _host_ident=$host_cpu esac ;; *-*-cygwin*) - _host_cpu= + _host_ident= + ;; + *-apple-ios*-simulator) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-iphonesimulator + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-iphonesimulator + esac + ;; + *-apple-ios*) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-iphoneos + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-iphoneos + esac + ;; + *-apple-tvos*-simulator) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-appletvsimulator + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-appletvsimulator + esac + ;; + *-apple-tvos*) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-appletvos + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-appletvos + esac + ;; + *-apple-watchos*-simulator) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-watchsimualtor + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-watchsimualtor + esac + ;; + *-apple-watchos*) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64_32-watchosos + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-watchosos + esac + ;; + *-apple-*) + case "$host_cpu" in + arm*) + _host_ident=arm + ;; + *) + _host_ident=$host_cpu + esac ;; *-*-vxworks*) - _host_cpu=$host_cpu + _host_ident=$host_cpu ;; wasm32-*-* | wasm64-*-*) - _host_cpu=$host_cpu + _host_ident=$host_cpu ;; *) # for now, limit cross builds to known configurations MACHDEP="unknown" as_fn_error $? "cross build not supported for $host" "$LINENO" 5 esac - _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" + _PYTHON_HOST_PLATFORM="$MACHDEP${_host_ident:+-$_host_ident}" fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they @@ -4413,6 +4491,13 @@ printf "%s\n" "#define _BSD_SOURCE 1" >>confdefs.h define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; + # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) + define_xopen_source=no;; # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) @@ -6904,7 +6989,42 @@ cat > conftest.c <&6; } case $ac_sys_system in #( Darwin*) : MULTIARCH="" ;; #( + iOS) : + MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : + MULTIARCH="" ;; #( FreeBSD*) : MULTIARCH="" ;; #( *) : @@ -6960,8 +7086,6 @@ case $ac_sys_system in #( ;; esac -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MULTIARCH" >&5 -printf "%s\n" "$MULTIARCH" >&6; } if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then if test x$PLATFORM_TRIPLET != x$MULTIARCH; then @@ -6971,6 +7095,16 @@ elif test x$PLATFORM_TRIPLET != x && test x$MULTIARCH = x; then MULTIARCH=$PLATFORM_TRIPLET fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MULTIARCH" >&5 +printf "%s\n" "$MULTIARCH" >&6; } + +case $ac_sys_system in #( + iOS|tvOS|watchOS) : + SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f1` ;; #( + *) : + SOABI_PLATFORM=$PLATFORM_TRIPLET + ;; +esac if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" @@ -7014,8 +7148,14 @@ case $host/$ac_cv_cc_name in #( PY_SUPPORT_TIER=3 ;; #( x86_64-*-freebsd*/clang) : PY_SUPPORT_TIER=3 ;; #( + aarch64-apple-ios*-simulator/clang) : + PY_SUPPORT_TIER=3 ;; #( + x86_64-apple-ios*-simulator/clang) : + PY_SUPPORT_TIER=3 ;; #( + aarch64-apple-ios*/clang) : + PY_SUPPORT_TIER=3 ;; #( *) : - PY_SUPPORT_TIER=0 + PY_SUPPORT_TIER=0 ;; esac @@ -12789,6 +12929,9 @@ then BLDSHARED="$LDSHARED" fi ;; + iOS/*|tvOS/*|watchOS/*) + LDSHARED='$(CC) -dynamiclib -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -dynamiclib -undefined dynamic_lookup';; Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; @@ -14330,6 +14473,10 @@ then : ctypes_malloc_closure=yes ;; #( + iOS|tvOS|watchOS) : + + ctypes_malloc_closure=yes + ;; #( sunos5) : as_fn_append LIBFFI_LIBS " -mimpure-text" ;; #( @@ -24377,7 +24524,7 @@ printf %s "checking ABIFLAGS... " >&6; } printf "%s\n" "$ABIFLAGS" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SOABI" >&5 printf %s "checking SOABI... " >&6; } -SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} +SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${SOABI_PLATFORM:+-$SOABI_PLATFORM} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SOABI" >&5 printf "%s\n" "$SOABI" >&6; } @@ -24385,7 +24532,7 @@ printf "%s\n" "$SOABI" >&6; } if test "$Py_DEBUG" = 'true' -a "$with_trace_refs" != "yes"; then # Similar to SOABI but remove "d" flag from ABIFLAGS - ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} + ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${SOABI_PLATFORM:+-$SOABI_PLATFORM} printf "%s\n" "#define ALT_SOABI \"${ALT_SOABI}\"" >>confdefs.h @@ -28586,6 +28733,29 @@ case $ac_sys_system in #( py_cv_module_ossaudiodev=n/a py_cv_module_spwd=n/a ;; #( + iOS|tvOS|watchOS) : + + + + py_cv_module__curses=n/a + py_cv_module__curses_panel=n/a + py_cv_module__gdbm=n/a + py_cv_module__multiprocessing=n/a + py_cv_module__posixshmem=n/a + py_cv_module__posixsubprocess=n/a + py_cv_module__scproxy=n/a + py_cv_module__tkinter=n/a + py_cv_module__xxsubinterpreters=n/a + py_cv_module_grp=n/a + py_cv_module_nis=n/a + py_cv_module_ossaudiodev=n/a + py_cv_module_readline=n/a + py_cv_module_pwd=n/a + py_cv_module_spwd=n/a + py_cv_module_syslog=n/a + py_cv_module_=n/a + + ;; #( CYGWIN*) : diff --git a/configure.ac b/configure.ac index 8be26cc0ab..14a8ec7c27 100644 --- a/configure.ac +++ b/configure.ac @@ -550,6 +550,15 @@ then *-*-cygwin*) ac_sys_system=Cygwin ;; + *-apple-ios*) + ac_sys_system=iOS + ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-vxworks*) ac_sys_system=VxWorks ;; @@ -604,27 +613,96 @@ if test "$cross_compiling" = yes; then *-*-linux*) case "$host_cpu" in arm*) - _host_cpu=arm + _host_ident=arm ;; *) - _host_cpu=$host_cpu + _host_ident=$host_cpu esac ;; *-*-cygwin*) - _host_cpu= + _host_ident= + ;; + *-apple-ios*-simulator) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-iphonesimulator + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-iphonesimulator + esac + ;; + *-apple-ios*) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-iphoneos + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-iphoneos + esac + ;; + *-apple-tvos*-simulator) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-appletvsimulator + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-appletvsimulator + esac + ;; + *-apple-tvos*) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-appletvos + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-appletvos + esac + ;; + *-apple-watchos*-simulator) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64-watchsimualtor + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-watchsimualtor + esac + ;; + *-apple-watchos*) + _host_os_min_version=`echo $host | cut -d '-' -f3` + case "$host_cpu" in + aarch64) + _host_ident=${_host_os_min_version:3}-arm64_32-watchosos + ;; + *) + _host_ident=${_host_os_min_version:3}-$host_cpu-watchosos + esac + ;; + *-apple-*) + case "$host_cpu" in + arm*) + _host_ident=arm + ;; + *) + _host_ident=$host_cpu + esac ;; *-*-vxworks*) - _host_cpu=$host_cpu + _host_ident=$host_cpu ;; wasm32-*-* | wasm64-*-*) - _host_cpu=$host_cpu + _host_ident=$host_cpu ;; *) # for now, limit cross builds to known configurations MACHDEP="unknown" AC_MSG_ERROR([cross build not supported for $host]) esac - _PYTHON_HOST_PLATFORM="$MACHDEP${_host_cpu:+-$_host_cpu}" + _PYTHON_HOST_PLATFORM="$MACHDEP${_host_ident:+-$_host_ident}" fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they @@ -690,6 +768,13 @@ case $ac_sys_system/$ac_sys_release in define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; + # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) + define_xopen_source=no;; # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) @@ -1077,7 +1162,42 @@ cat > conftest.c </dev/null)] ) AC_SUBST([MULTIARCH]) -AC_MSG_RESULT([$MULTIARCH]) if test x$PLATFORM_TRIPLET != x && test x$MULTIARCH != x; then if test x$PLATFORM_TRIPLET != x$MULTIARCH; then @@ -1136,6 +1258,12 @@ elif test x$PLATFORM_TRIPLET != x && test x$MULTIARCH = x; then MULTIARCH=$PLATFORM_TRIPLET fi AC_SUBST([PLATFORM_TRIPLET]) +AC_MSG_RESULT([$MULTIARCH]) + +AS_CASE([$ac_sys_system], + [iOS|tvOS|watchOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f1`], + [SOABI_PLATFORM=$PLATFORM_TRIPLET] +) if test x$MULTIARCH != x; then MULTIARCH_CPPFLAGS="-DMULTIARCH=\\\"$MULTIARCH\\\"" @@ -1166,6 +1294,15 @@ AS_CASE([$host/$ac_cv_cc_name], [wasm32-unknown-emscripten/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly Emscripten [wasm32-unknown-wasi/clang], [PY_SUPPORT_TIER=3], dnl WebAssembly System Interface [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 + [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 + [x86_64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on x86_64 + [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 + dnl [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple tvOS Simulator on ARM64 + dnl [x86_64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple tvOS Simulator on x86_64 + dnl [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl Apple tvOS on ARM64 + dnl [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple watchOS Simulator on ARM64 + dnl [x86_64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl Apple watchOS Simulator on x86_64 + dnl [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl Apple watchOS on ARM64_32 [PY_SUPPORT_TIER=0] ) @@ -3348,6 +3485,9 @@ then BLDSHARED="$LDSHARED" fi ;; + iOS/*|tvOS/*|watchOS/*) + LDSHARED='$(CC) -dynamiclib -undefined dynamic_lookup' + LDCXXSHARED='$(CXX) -dynamiclib -undefined dynamic_lookup';; Emscripten|WASI) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; @@ -3865,6 +4005,9 @@ AS_VAR_IF([have_libffi], [yes], [ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], + [iOS|tvOS|watchOS], [ + ctypes_malloc_closure=yes + ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] ) AS_VAR_IF([ctypes_malloc_closure], [yes], [ @@ -5960,14 +6103,14 @@ AC_SUBST([SOABI]) AC_MSG_CHECKING([ABIFLAGS]) AC_MSG_RESULT([$ABIFLAGS]) AC_MSG_CHECKING([SOABI]) -SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} +SOABI='cpython-'`echo $VERSION | tr -d .`${ABIFLAGS}${SOABI_PLATFORM:+-$SOABI_PLATFORM} AC_MSG_RESULT([$SOABI]) # Release and debug (Py_DEBUG) ABI are compatible, but not Py_TRACE_REFS ABI if test "$Py_DEBUG" = 'true' -a "$with_trace_refs" != "yes"; then # Similar to SOABI but remove "d" flag from ABIFLAGS AC_SUBST([ALT_SOABI]) - ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${PLATFORM_TRIPLET:+-$PLATFORM_TRIPLET} + ALT_SOABI='cpython-'`echo $VERSION | tr -d .``echo $ABIFLAGS | tr -d d`${SOABI_PLATFORM:+-$SOABI_PLATFORM} AC_DEFINE_UNQUOTED([ALT_SOABI], ["${ALT_SOABI}"], [Alternative SOABI used in debug build to load C extensions built in release mode]) fi @@ -7251,6 +7394,30 @@ AS_CASE([$ac_sys_system], [AIX], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])], [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [_crypt], [termios], [grp])], [Darwin], [PY_STDLIB_MOD_SET_NA([ossaudiodev], [spwd])], + [iOS|tvOS|watchOS], [ + dnl subprocess and multiprocessing are not supported (no fork syscall). + dnl curses and tkinter user interface are not available. + dnl gdbm, and nis aren't available + dnl Stub implementations are provided for pwd, grp etc APIs + PY_STDLIB_MOD_SET_NA( + [_curses], + [_curses_panel], + [_gdbm], + [_multiprocessing], + [_posixshmem], + [_posixsubprocess], + [_scproxy], + [_tkinter], + [_xxsubinterpreters], + [grp], + [nis], + [ossaudiodev], + [readline], + [pwd], + [spwd], + [syslog], + ) + ], [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy], [nis])], [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy], [spwd])],