mirror of https://github.com/python/cpython
gh-114099: Additions to standard library to support iOS (GH-117052)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Malcolm Smith <smith@chaquo.com> Co-authored-by: Ned Deily <nad@python.org>
This commit is contained in:
parent
b44898299a
commit
f006338017
|
@ -784,6 +784,11 @@ process and user.
|
||||||
:func:`socket.gethostname` or even
|
:func:`socket.gethostname` or even
|
||||||
``socket.gethostbyaddr(socket.gethostname())``.
|
``socket.gethostbyaddr(socket.gethostname())``.
|
||||||
|
|
||||||
|
On macOS, iOS and Android, this returns the *kernel* name and version (i.e.,
|
||||||
|
``'Darwin'`` on macOS and iOS; ``'Linux'`` on Android). :func:`platform.uname()`
|
||||||
|
can be used to get the user-facing operating system name and version on iOS and
|
||||||
|
Android.
|
||||||
|
|
||||||
.. availability:: Unix.
|
.. availability:: Unix.
|
||||||
|
|
||||||
.. versionchanged:: 3.3
|
.. versionchanged:: 3.3
|
||||||
|
|
|
@ -148,6 +148,9 @@ Cross Platform
|
||||||
Returns the system/OS name, such as ``'Linux'``, ``'Darwin'``, ``'Java'``,
|
Returns the system/OS name, such as ``'Linux'``, ``'Darwin'``, ``'Java'``,
|
||||||
``'Windows'``. An empty string is returned if the value cannot be determined.
|
``'Windows'``. An empty string is returned if the value cannot be determined.
|
||||||
|
|
||||||
|
On iOS and Android, this returns the user-facing OS name (i.e, ``'iOS``,
|
||||||
|
``'iPadOS'`` or ``'Android'``). To obtain the kernel name (``'Darwin'`` or
|
||||||
|
``'Linux'``), use :func:`os.uname()`.
|
||||||
|
|
||||||
.. function:: system_alias(system, release, version)
|
.. function:: system_alias(system, release, version)
|
||||||
|
|
||||||
|
@ -161,6 +164,8 @@ Cross Platform
|
||||||
Returns the system's release version, e.g. ``'#3 on degas'``. An empty string is
|
Returns the system's release version, e.g. ``'#3 on degas'``. An empty string is
|
||||||
returned if the value cannot be determined.
|
returned if the value cannot be determined.
|
||||||
|
|
||||||
|
On iOS and Android, this is the user-facing OS version. To obtain the
|
||||||
|
Darwin or Linux kernel version, use :func:`os.uname()`.
|
||||||
|
|
||||||
.. function:: uname()
|
.. function:: uname()
|
||||||
|
|
||||||
|
@ -238,7 +243,6 @@ Windows Platform
|
||||||
macOS Platform
|
macOS Platform
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
||||||
.. function:: mac_ver(release='', versioninfo=('','',''), machine='')
|
.. function:: mac_ver(release='', versioninfo=('','',''), machine='')
|
||||||
|
|
||||||
Get macOS version information and return it as tuple ``(release, versioninfo,
|
Get macOS version information and return it as tuple ``(release, versioninfo,
|
||||||
|
@ -248,6 +252,24 @@ macOS Platform
|
||||||
Entries which cannot be determined are set to ``''``. All tuple entries are
|
Entries which cannot be determined are set to ``''``. All tuple entries are
|
||||||
strings.
|
strings.
|
||||||
|
|
||||||
|
iOS Platform
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. function:: ios_ver(system='', release='', model='', is_simulator=False)
|
||||||
|
|
||||||
|
Get iOS version information and return it as a
|
||||||
|
:func:`~collections.namedtuple` with the following attributes:
|
||||||
|
|
||||||
|
* ``system`` is the OS name; either ``'iOS'`` or ``'iPadOS'``.
|
||||||
|
* ``release`` is the iOS version number as a string (e.g., ``'17.2'``).
|
||||||
|
* ``model`` is the device model identifier; this will be a string like
|
||||||
|
``'iPhone13,2'`` for a physical device, or ``'iPhone'`` on a simulator.
|
||||||
|
* ``is_simulator`` is a boolean describing if the app is running on a
|
||||||
|
simulator or a physical device.
|
||||||
|
|
||||||
|
Entries which cannot be determined are set to the defaults given as
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
|
||||||
Unix Platforms
|
Unix Platforms
|
||||||
--------------
|
--------------
|
||||||
|
|
|
@ -33,6 +33,13 @@ allow the remote browser to maintain its own windows on the display. If remote
|
||||||
browsers are not available on Unix, the controlling process will launch a new
|
browsers are not available on Unix, the controlling process will launch a new
|
||||||
browser and wait.
|
browser and wait.
|
||||||
|
|
||||||
|
On iOS, the :envvar:`BROWSER` environment variable, as well as any arguments
|
||||||
|
controlling autoraise, browser preference, and new tab/window creation will be
|
||||||
|
ignored. Web pages will *always* be opened in the user's preferred browser, in
|
||||||
|
a new tab, with the browser being brought to the foreground. The use of the
|
||||||
|
:mod:`webbrowser` module on iOS requires the :mod:`ctypes` module. If
|
||||||
|
:mod:`ctypes` isn't available, calls to :func:`.open` will fail.
|
||||||
|
|
||||||
The script :program:`webbrowser` can be used as a command-line interface for the
|
The script :program:`webbrowser` can be used as a command-line interface for the
|
||||||
module. It accepts a URL as the argument. It accepts the following optional
|
module. It accepts a URL as the argument. It accepts the following optional
|
||||||
parameters: ``-n`` opens the URL in a new browser window, if possible;
|
parameters: ``-n`` opens the URL in a new browser window, if possible;
|
||||||
|
@ -147,6 +154,8 @@ for the controller classes, all defined in this module.
|
||||||
+------------------------+-----------------------------------------+-------+
|
+------------------------+-----------------------------------------+-------+
|
||||||
| ``'chromium-browser'`` | ``Chromium('chromium-browser')`` | |
|
| ``'chromium-browser'`` | ``Chromium('chromium-browser')`` | |
|
||||||
+------------------------+-----------------------------------------+-------+
|
+------------------------+-----------------------------------------+-------+
|
||||||
|
| ``'iosbrowser'`` | ``IOSBrowser`` | \(4) |
|
||||||
|
+------------------------+-----------------------------------------+-------+
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
|
@ -161,7 +170,10 @@ Notes:
|
||||||
Only on Windows platforms.
|
Only on Windows platforms.
|
||||||
|
|
||||||
(3)
|
(3)
|
||||||
Only on macOS platform.
|
Only on macOS.
|
||||||
|
|
||||||
|
(4)
|
||||||
|
Only on iOS.
|
||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
A new :class:`!MacOSXOSAScript` class has been added
|
A new :class:`!MacOSXOSAScript` class has been added
|
||||||
|
@ -176,6 +188,9 @@ Notes:
|
||||||
Removed browsers include Grail, Mosaic, Netscape, Galeon,
|
Removed browsers include Grail, Mosaic, Netscape, Galeon,
|
||||||
Skipstone, Iceape, and Firefox versions 35 and below.
|
Skipstone, Iceape, and Firefox versions 35 and below.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.13
|
||||||
|
Support for iOS has been added.
|
||||||
|
|
||||||
Here are some simple examples::
|
Here are some simple examples::
|
||||||
|
|
||||||
url = 'https://docs.python.org/'
|
url = 'https://docs.python.org/'
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
from ctypes import cdll, c_void_p, c_char_p, util
|
||||||
|
except ImportError:
|
||||||
|
# ctypes is an optional module. If it's not present, we're limited in what
|
||||||
|
# we can tell about the system, but we don't want to prevent the module
|
||||||
|
# from working.
|
||||||
|
print("ctypes isn't available; iOS system calls will not be available")
|
||||||
|
objc = None
|
||||||
|
else:
|
||||||
|
# ctypes is available. Load the ObjC library, and wrap the objc_getClass,
|
||||||
|
# sel_registerName methods
|
||||||
|
lib = util.find_library("objc")
|
||||||
|
if lib is None:
|
||||||
|
# Failed to load the objc library
|
||||||
|
raise RuntimeError("ObjC runtime library couldn't be loaded")
|
||||||
|
|
||||||
|
objc = cdll.LoadLibrary(lib)
|
||||||
|
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]
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_ios():
|
||||||
|
# Determine if this is a simulator using the multiarch value
|
||||||
|
is_simulator = sys.implementation._multiarch.endswith("simulator")
|
||||||
|
|
||||||
|
# We can't use ctypes; abort
|
||||||
|
if not objc:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Most of the methods return ObjC objects
|
||||||
|
objc.objc_msgSend.restype = c_void_p
|
||||||
|
# All the methods used have no arguments.
|
||||||
|
objc.objc_msgSend.argtypes = [c_void_p, c_void_p]
|
||||||
|
|
||||||
|
# Equivalent of:
|
||||||
|
# device = [UIDevice currentDevice]
|
||||||
|
UIDevice = objc.objc_getClass(b"UIDevice")
|
||||||
|
SEL_currentDevice = objc.sel_registerName(b"currentDevice")
|
||||||
|
device = objc.objc_msgSend(UIDevice, SEL_currentDevice)
|
||||||
|
|
||||||
|
# Equivalent of:
|
||||||
|
# device_systemVersion = [device systemVersion]
|
||||||
|
SEL_systemVersion = objc.sel_registerName(b"systemVersion")
|
||||||
|
device_systemVersion = objc.objc_msgSend(device, SEL_systemVersion)
|
||||||
|
|
||||||
|
# Equivalent of:
|
||||||
|
# device_systemName = [device systemName]
|
||||||
|
SEL_systemName = objc.sel_registerName(b"systemName")
|
||||||
|
device_systemName = objc.objc_msgSend(device, SEL_systemName)
|
||||||
|
|
||||||
|
# Equivalent of:
|
||||||
|
# device_model = [device model]
|
||||||
|
SEL_model = objc.sel_registerName(b"model")
|
||||||
|
device_model = objc.objc_msgSend(device, SEL_model)
|
||||||
|
|
||||||
|
# UTF8String returns a const char*;
|
||||||
|
SEL_UTF8String = objc.sel_registerName(b"UTF8String")
|
||||||
|
objc.objc_msgSend.restype = c_char_p
|
||||||
|
|
||||||
|
# Equivalent of:
|
||||||
|
# system = [device_systemName UTF8String]
|
||||||
|
# release = [device_systemVersion UTF8String]
|
||||||
|
# model = [device_model UTF8String]
|
||||||
|
system = objc.objc_msgSend(device_systemName, SEL_UTF8String).decode()
|
||||||
|
release = objc.objc_msgSend(device_systemVersion, SEL_UTF8String).decode()
|
||||||
|
model = objc.objc_msgSend(device_model, SEL_UTF8String).decode()
|
||||||
|
|
||||||
|
return system, release, model, is_simulator
|
|
@ -496,6 +496,30 @@ def mac_ver(release='', versioninfo=('', '', ''), machine=''):
|
||||||
# If that also doesn't work return the default values
|
# If that also doesn't work return the default values
|
||||||
return release, versioninfo, machine
|
return release, versioninfo, machine
|
||||||
|
|
||||||
|
|
||||||
|
# A namedtuple for iOS version information.
|
||||||
|
IOSVersionInfo = collections.namedtuple(
|
||||||
|
"IOSVersionInfo",
|
||||||
|
["system", "release", "model", "is_simulator"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ios_ver(system="", release="", model="", is_simulator=False):
|
||||||
|
"""Get iOS version information, and return it as a namedtuple:
|
||||||
|
(system, release, model, is_simulator).
|
||||||
|
|
||||||
|
If values can't be determined, they are set to values provided as
|
||||||
|
parameters.
|
||||||
|
"""
|
||||||
|
if sys.platform == "ios":
|
||||||
|
import _ios_support
|
||||||
|
result = _ios_support.get_platform_ios()
|
||||||
|
if result is not None:
|
||||||
|
return IOSVersionInfo(*result)
|
||||||
|
|
||||||
|
return IOSVersionInfo(system, release, model, is_simulator)
|
||||||
|
|
||||||
|
|
||||||
def _java_getprop(name, default):
|
def _java_getprop(name, default):
|
||||||
"""This private helper is deprecated in 3.13 and will be removed in 3.15"""
|
"""This private helper is deprecated in 3.13 and will be removed in 3.15"""
|
||||||
from java.lang import System
|
from java.lang import System
|
||||||
|
@ -654,7 +678,7 @@ def _platform(*args):
|
||||||
if cleaned == platform:
|
if cleaned == platform:
|
||||||
break
|
break
|
||||||
platform = cleaned
|
platform = cleaned
|
||||||
while platform[-1] == '-':
|
while platform and platform[-1] == '-':
|
||||||
platform = platform[:-1]
|
platform = platform[:-1]
|
||||||
|
|
||||||
return platform
|
return platform
|
||||||
|
@ -695,7 +719,7 @@ def _syscmd_file(target, default=''):
|
||||||
default in case the command should fail.
|
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 ?
|
# XXX Others too ?
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
@ -859,6 +883,14 @@ class _Processor:
|
||||||
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
|
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
|
||||||
return 'Alpha' if cpu_number >= 128 else 'VAX'
|
return 'Alpha' if cpu_number >= 128 else 'VAX'
|
||||||
|
|
||||||
|
# On the iOS simulator, os.uname returns the architecture as uname.machine.
|
||||||
|
# On device it returns the model name for some reason; but there's only one
|
||||||
|
# CPU architecture for iOS devices, so we know the right answer.
|
||||||
|
def get_ios():
|
||||||
|
if sys.implementation._multiarch.endswith("simulator"):
|
||||||
|
return os.uname().machine
|
||||||
|
return 'arm64'
|
||||||
|
|
||||||
def from_subprocess():
|
def from_subprocess():
|
||||||
"""
|
"""
|
||||||
Fall back to `uname -p`
|
Fall back to `uname -p`
|
||||||
|
@ -1018,6 +1050,10 @@ def uname():
|
||||||
system = 'Android'
|
system = 'Android'
|
||||||
release = android_ver().release
|
release = android_ver().release
|
||||||
|
|
||||||
|
# Normalize responses on iOS
|
||||||
|
if sys.platform == 'ios':
|
||||||
|
system, release, _, _ = ios_ver()
|
||||||
|
|
||||||
vals = system, node, release, version, machine
|
vals = system, node, release, version, machine
|
||||||
# Replace 'unknown' values with the more portable ''
|
# Replace 'unknown' values with the more portable ''
|
||||||
_uname_cache = uname_result(*map(_unknown_as_blank, vals))
|
_uname_cache = uname_result(*map(_unknown_as_blank, vals))
|
||||||
|
@ -1297,11 +1333,14 @@ def platform(aliased=False, terse=False):
|
||||||
system, release, version = system_alias(system, release, version)
|
system, release, version = system_alias(system, release, version)
|
||||||
|
|
||||||
if system == 'Darwin':
|
if system == 'Darwin':
|
||||||
# macOS (darwin kernel)
|
# macOS and iOS both report as a "Darwin" kernel
|
||||||
macos_release = mac_ver()[0]
|
if sys.platform == "ios":
|
||||||
if macos_release:
|
system, release, _, _ = ios_ver()
|
||||||
system = 'macOS'
|
else:
|
||||||
release = macos_release
|
macos_release = mac_ver()[0]
|
||||||
|
if macos_release:
|
||||||
|
system = 'macOS'
|
||||||
|
release = macos_release
|
||||||
|
|
||||||
if system == 'Windows':
|
if system == 'Windows':
|
||||||
# MS platforms
|
# MS platforms
|
||||||
|
|
|
@ -280,8 +280,8 @@ def _getuserbase():
|
||||||
if env_base:
|
if env_base:
|
||||||
return env_base
|
return env_base
|
||||||
|
|
||||||
# Emscripten, VxWorks, and WASI have no home directories
|
# Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
|
||||||
if sys.platform in {"emscripten", "vxworks", "wasi"}:
|
if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def joinuser(*args):
|
def joinuser(*args):
|
||||||
|
|
|
@ -21,6 +21,7 @@ __all__ = [
|
||||||
|
|
||||||
# Keys for get_config_var() that are never converted to Python integers.
|
# Keys for get_config_var() that are never converted to Python integers.
|
||||||
_ALWAYS_STR = {
|
_ALWAYS_STR = {
|
||||||
|
'IPHONEOS_DEPLOYMENT_TARGET',
|
||||||
'MACOSX_DEPLOYMENT_TARGET',
|
'MACOSX_DEPLOYMENT_TARGET',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ _INSTALL_SCHEMES = {
|
||||||
'scripts': '{base}/Scripts',
|
'scripts': '{base}/Scripts',
|
||||||
'data': '{base}',
|
'data': '{base}',
|
||||||
},
|
},
|
||||||
|
|
||||||
# Downstream distributors can overwrite the default install scheme.
|
# Downstream distributors can overwrite the default install scheme.
|
||||||
# This is done to support downstream modifications where distributors change
|
# This is done to support downstream modifications where distributors change
|
||||||
# the installation layout (eg. different site-packages directory).
|
# the installation layout (eg. different site-packages directory).
|
||||||
|
@ -114,8 +116,8 @@ def _getuserbase():
|
||||||
if env_base:
|
if env_base:
|
||||||
return env_base
|
return env_base
|
||||||
|
|
||||||
# Emscripten, VxWorks, and WASI have no home directories
|
# Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
|
||||||
if sys.platform in {"emscripten", "vxworks", "wasi"}:
|
if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def joinuser(*args):
|
def joinuser(*args):
|
||||||
|
@ -290,6 +292,7 @@ def _get_preferred_schemes():
|
||||||
'home': 'posix_home',
|
'home': 'posix_home',
|
||||||
'user': 'osx_framework_user',
|
'user': 'osx_framework_user',
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'prefix': 'posix_prefix',
|
'prefix': 'posix_prefix',
|
||||||
'home': 'posix_home',
|
'home': 'posix_home',
|
||||||
|
@ -623,10 +626,15 @@ def get_platform():
|
||||||
if m:
|
if m:
|
||||||
release = m.group()
|
release = m.group()
|
||||||
elif osname[:6] == "darwin":
|
elif osname[:6] == "darwin":
|
||||||
import _osx_support
|
if sys.platform == "ios":
|
||||||
osname, release, machine = _osx_support.get_platform_osx(
|
release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "12.0")
|
||||||
get_config_vars(),
|
osname = sys.platform
|
||||||
osname, release, machine)
|
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}"
|
return f"{osname}-{release}-{machine}"
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,7 @@ def collect_os(info_add):
|
||||||
"HOMEDRIVE",
|
"HOMEDRIVE",
|
||||||
"HOMEPATH",
|
"HOMEPATH",
|
||||||
"IDLESTARTUP",
|
"IDLESTARTUP",
|
||||||
|
"IPHONEOS_DEPLOYMENT_TARGET",
|
||||||
"LANG",
|
"LANG",
|
||||||
"LDFLAGS",
|
"LDFLAGS",
|
||||||
"LDSHARED",
|
"LDSHARED",
|
||||||
|
|
|
@ -49,6 +49,7 @@ class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest, BaseTestCase):
|
||||||
self.assertEqual(len(executor._threads), 1)
|
self.assertEqual(len(executor._threads), 1)
|
||||||
executor.shutdown(wait=True)
|
executor.shutdown(wait=True)
|
||||||
|
|
||||||
|
@support.requires_fork()
|
||||||
@unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork')
|
@unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork')
|
||||||
@support.requires_resource('cpu')
|
@support.requires_resource('cpu')
|
||||||
def test_hang_global_shutdown_lock(self):
|
def test_hang_global_shutdown_lock(self):
|
||||||
|
|
|
@ -1223,6 +1223,7 @@ class GCCallbackTests(unittest.TestCase):
|
||||||
self.assertEqual(len(gc.garbage), 0)
|
self.assertEqual(len(gc.garbage), 0)
|
||||||
|
|
||||||
|
|
||||||
|
@requires_subprocess()
|
||||||
@unittest.skipIf(BUILD_WITH_NDEBUG,
|
@unittest.skipIf(BUILD_WITH_NDEBUG,
|
||||||
'built with -NDEBUG')
|
'built with -NDEBUG')
|
||||||
def test_refcount_errors(self):
|
def test_refcount_errors(self):
|
||||||
|
|
|
@ -10,6 +10,14 @@ from unittest import mock
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Some of the iOS tests need ctypes to operate.
|
||||||
|
# Confirm that the ctypes module is available
|
||||||
|
# is available.
|
||||||
|
import _ctypes
|
||||||
|
except ImportError:
|
||||||
|
_ctypes = None
|
||||||
|
|
||||||
FEDORA_OS_RELEASE = """\
|
FEDORA_OS_RELEASE = """\
|
||||||
NAME=Fedora
|
NAME=Fedora
|
||||||
VERSION="32 (Thirty Two)"
|
VERSION="32 (Thirty Two)"
|
||||||
|
@ -228,10 +236,21 @@ class PlatformTest(unittest.TestCase):
|
||||||
if sys.platform == "android":
|
if sys.platform == "android":
|
||||||
self.assertEqual(res.system, "Android")
|
self.assertEqual(res.system, "Android")
|
||||||
self.assertEqual(res.release, platform.android_ver().release)
|
self.assertEqual(res.release, platform.android_ver().release)
|
||||||
|
elif sys.platform == "ios":
|
||||||
|
# Platform module needs ctypes for full operation. If ctypes
|
||||||
|
# isn't available, there's no ObjC module, and dummy values are
|
||||||
|
# returned.
|
||||||
|
if _ctypes:
|
||||||
|
self.assertIn(res.system, {"iOS", "iPadOS"})
|
||||||
|
self.assertEqual(res.release, platform.ios_ver().release)
|
||||||
|
else:
|
||||||
|
self.assertEqual(res.system, "")
|
||||||
|
self.assertEqual(res.release, "")
|
||||||
else:
|
else:
|
||||||
self.assertEqual(res.system, uname.sysname)
|
self.assertEqual(res.system, uname.sysname)
|
||||||
self.assertEqual(res.release, uname.release)
|
self.assertEqual(res.release, uname.release)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
|
@unittest.skipUnless(sys.platform.startswith('win'), "windows only test")
|
||||||
def test_uname_win32_without_wmi(self):
|
def test_uname_win32_without_wmi(self):
|
||||||
def raises_oserror(*a):
|
def raises_oserror(*a):
|
||||||
|
@ -422,6 +441,56 @@ class PlatformTest(unittest.TestCase):
|
||||||
# parent
|
# parent
|
||||||
support.wait_process(pid, exitcode=0)
|
support.wait_process(pid, exitcode=0)
|
||||||
|
|
||||||
|
def test_ios_ver(self):
|
||||||
|
result = platform.ios_ver()
|
||||||
|
|
||||||
|
# ios_ver is only fully available on iOS where ctypes is available.
|
||||||
|
if sys.platform == "ios" and _ctypes:
|
||||||
|
system, release, model, is_simulator = result
|
||||||
|
# Result is a namedtuple
|
||||||
|
self.assertEqual(result.system, system)
|
||||||
|
self.assertEqual(result.release, release)
|
||||||
|
self.assertEqual(result.model, model)
|
||||||
|
self.assertEqual(result.is_simulator, is_simulator)
|
||||||
|
|
||||||
|
# We can't assert specific values without reproducing the logic of
|
||||||
|
# ios_ver(), so we check that the values are broadly what we expect.
|
||||||
|
|
||||||
|
# System is either iOS or iPadOS, depending on the test device
|
||||||
|
self.assertIn(system, {"iOS", "iPadOS"})
|
||||||
|
|
||||||
|
# Release is a numeric version specifier with at least 2 parts
|
||||||
|
parts = release.split(".")
|
||||||
|
self.assertGreaterEqual(len(parts), 2)
|
||||||
|
self.assertTrue(all(part.isdigit() for part in parts))
|
||||||
|
|
||||||
|
# If this is a simulator, we get a high level device descriptor
|
||||||
|
# with no identifying model number. If this is a physical device,
|
||||||
|
# we get a model descriptor like "iPhone13,1"
|
||||||
|
if is_simulator:
|
||||||
|
self.assertIn(model, {"iPhone", "iPad"})
|
||||||
|
else:
|
||||||
|
self.assertTrue(
|
||||||
|
(model.startswith("iPhone") or model.startswith("iPad"))
|
||||||
|
and "," in model
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(type(is_simulator), bool)
|
||||||
|
else:
|
||||||
|
# On non-iOS platforms, calling ios_ver doesn't fail; you get
|
||||||
|
# default values
|
||||||
|
self.assertEqual(result.system, "")
|
||||||
|
self.assertEqual(result.release, "")
|
||||||
|
self.assertEqual(result.model, "")
|
||||||
|
self.assertFalse(result.is_simulator)
|
||||||
|
|
||||||
|
# Check the fallback values can be overridden by arguments
|
||||||
|
override = platform.ios_ver("Foo", "Bar", "Whiz", True)
|
||||||
|
self.assertEqual(override.system, "Foo")
|
||||||
|
self.assertEqual(override.release, "Bar")
|
||||||
|
self.assertEqual(override.model, "Whiz")
|
||||||
|
self.assertTrue(override.is_simulator)
|
||||||
|
|
||||||
@unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten")
|
@unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten")
|
||||||
def test_libc_ver(self):
|
def test_libc_ver(self):
|
||||||
# check that libc_ver(executable) doesn't raise an exception
|
# check that libc_ver(executable) doesn't raise an exception
|
||||||
|
|
|
@ -8,7 +8,11 @@ import shutil
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
from test.support import (
|
from test.support import (
|
||||||
captured_stdout, PythonSymlink, requires_subprocess, is_wasi
|
captured_stdout,
|
||||||
|
is_apple_mobile,
|
||||||
|
is_wasi,
|
||||||
|
PythonSymlink,
|
||||||
|
requires_subprocess,
|
||||||
)
|
)
|
||||||
from test.support.import_helper import import_module
|
from test.support.import_helper import import_module
|
||||||
from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink,
|
from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink,
|
||||||
|
@ -346,6 +350,8 @@ class TestSysConfig(unittest.TestCase):
|
||||||
# XXX more platforms to tests here
|
# XXX more platforms to tests here
|
||||||
|
|
||||||
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
|
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
|
||||||
|
@unittest.skipIf(is_apple_mobile,
|
||||||
|
f"{sys.platform} doesn't distribute header files in the runtime environment")
|
||||||
def test_get_config_h_filename(self):
|
def test_get_config_h_filename(self):
|
||||||
config_h = sysconfig.get_config_h_filename()
|
config_h = sysconfig.get_config_h_filename()
|
||||||
self.assertTrue(os.path.isfile(config_h), config_h)
|
self.assertTrue(os.path.isfile(config_h), config_h)
|
||||||
|
@ -423,6 +429,9 @@ class TestSysConfig(unittest.TestCase):
|
||||||
self.assertTrue(library.startswith(f'python{major}{minor}'))
|
self.assertTrue(library.startswith(f'python{major}{minor}'))
|
||||||
self.assertTrue(library.endswith('.dll'))
|
self.assertTrue(library.endswith('.dll'))
|
||||||
self.assertEqual(library, ldlibrary)
|
self.assertEqual(library, ldlibrary)
|
||||||
|
elif is_apple_mobile:
|
||||||
|
framework = sysconfig.get_config_var('PYTHONFRAMEWORK')
|
||||||
|
self.assertEqual(ldlibrary, f"{framework}.framework/{framework}")
|
||||||
else:
|
else:
|
||||||
self.assertTrue(library.startswith(f'libpython{major}.{minor}'))
|
self.assertTrue(library.startswith(f'libpython{major}.{minor}'))
|
||||||
self.assertTrue(library.endswith('.a'))
|
self.assertTrue(library.endswith('.a'))
|
||||||
|
@ -476,6 +485,8 @@ class TestSysConfig(unittest.TestCase):
|
||||||
self.assertEqual(my_platform, test_platform)
|
self.assertEqual(my_platform, test_platform)
|
||||||
|
|
||||||
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
|
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
|
||||||
|
@unittest.skipIf(is_apple_mobile,
|
||||||
|
f"{sys.platform} doesn't include config folder at runtime")
|
||||||
def test_srcdir(self):
|
def test_srcdir(self):
|
||||||
# See Issues #15322, #15364.
|
# See Issues #15322, #15364.
|
||||||
srcdir = sysconfig.get_config_var('srcdir')
|
srcdir = sysconfig.get_config_var('srcdir')
|
||||||
|
@ -556,6 +567,8 @@ class MakefileTests(unittest.TestCase):
|
||||||
@unittest.skipIf(sys.platform.startswith('win'),
|
@unittest.skipIf(sys.platform.startswith('win'),
|
||||||
'Test is not Windows compatible')
|
'Test is not Windows compatible')
|
||||||
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
|
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
|
||||||
|
@unittest.skipIf(is_apple_mobile,
|
||||||
|
f"{sys.platform} doesn't include config folder at runtime")
|
||||||
def test_get_makefile_filename(self):
|
def test_get_makefile_filename(self):
|
||||||
makefile = sysconfig.get_makefile_filename()
|
makefile = sysconfig.get_makefile_filename()
|
||||||
self.assertTrue(os.path.isfile(makefile), makefile)
|
self.assertTrue(os.path.isfile(makefile), makefile)
|
||||||
|
|
|
@ -5,11 +5,14 @@ import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from test import support
|
from test import support
|
||||||
|
from test.support import is_apple_mobile
|
||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
from test.support import requires_subprocess
|
||||||
|
from test.support import threading_helper
|
||||||
|
|
||||||
if not support.has_subprocess_support:
|
# The webbrowser module uses threading locks
|
||||||
raise unittest.SkipTest("test webserver requires subprocess")
|
threading_helper.requires_working_threading(module=True)
|
||||||
|
|
||||||
URL = 'https://www.example.com'
|
URL = 'https://www.example.com'
|
||||||
CMD_NAME = 'test'
|
CMD_NAME = 'test'
|
||||||
|
@ -24,6 +27,7 @@ class PopenMock(mock.MagicMock):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@requires_subprocess()
|
||||||
class CommandTestMixin:
|
class CommandTestMixin:
|
||||||
|
|
||||||
def _test(self, meth, *, args=[URL], kw={}, options, arguments):
|
def _test(self, meth, *, args=[URL], kw={}, options, arguments):
|
||||||
|
@ -219,6 +223,73 @@ class ELinksCommandTest(CommandTestMixin, unittest.TestCase):
|
||||||
arguments=['openURL({},new-tab)'.format(URL)])
|
arguments=['openURL({},new-tab)'.format(URL)])
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(sys.platform == "ios", "Test only applicable to iOS")
|
||||||
|
class IOSBrowserTest(unittest.TestCase):
|
||||||
|
def _obj_ref(self, *args):
|
||||||
|
# Construct a string representation of the arguments that can be used
|
||||||
|
# as a proxy for object instance references
|
||||||
|
return "|".join(str(a) for a in args)
|
||||||
|
|
||||||
|
@unittest.skipIf(getattr(webbrowser, "objc", None) is None,
|
||||||
|
"iOS Webbrowser tests require ctypes")
|
||||||
|
def setUp(self):
|
||||||
|
# Intercept the the objc library. Wrap the calls to get the
|
||||||
|
# references to classes and selectors to return strings, and
|
||||||
|
# wrap msgSend to return stringified object references
|
||||||
|
self.orig_objc = webbrowser.objc
|
||||||
|
|
||||||
|
webbrowser.objc = mock.Mock()
|
||||||
|
webbrowser.objc.objc_getClass = lambda cls: f"C#{cls.decode()}"
|
||||||
|
webbrowser.objc.sel_registerName = lambda sel: f"S#{sel.decode()}"
|
||||||
|
webbrowser.objc.objc_msgSend.side_effect = self._obj_ref
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
webbrowser.objc = self.orig_objc
|
||||||
|
|
||||||
|
def _test(self, meth, **kwargs):
|
||||||
|
# The browser always gets focus, there's no concept of separate browser
|
||||||
|
# windows, and there's no API-level control over creating a new tab.
|
||||||
|
# Therefore, all calls to webbrowser are effectively the same.
|
||||||
|
getattr(webbrowser, meth)(URL, **kwargs)
|
||||||
|
|
||||||
|
# The ObjC String version of the URL is created with UTF-8 encoding
|
||||||
|
url_string_args = [
|
||||||
|
"C#NSString",
|
||||||
|
"S#stringWithCString:encoding:",
|
||||||
|
b'https://www.example.com',
|
||||||
|
4,
|
||||||
|
]
|
||||||
|
# The NSURL version of the URL is created from that string
|
||||||
|
url_obj_args = [
|
||||||
|
"C#NSURL",
|
||||||
|
"S#URLWithString:",
|
||||||
|
self._obj_ref(*url_string_args),
|
||||||
|
]
|
||||||
|
# The openURL call is invoked on the shared application
|
||||||
|
shared_app_args = ["C#UIApplication", "S#sharedApplication"]
|
||||||
|
|
||||||
|
# Verify that the last call is the one that opens the URL.
|
||||||
|
webbrowser.objc.objc_msgSend.assert_called_with(
|
||||||
|
self._obj_ref(*shared_app_args),
|
||||||
|
"S#openURL:options:completionHandler:",
|
||||||
|
self._obj_ref(*url_obj_args),
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_open(self):
|
||||||
|
self._test('open')
|
||||||
|
|
||||||
|
def test_open_with_autoraise_false(self):
|
||||||
|
self._test('open', autoraise=False)
|
||||||
|
|
||||||
|
def test_open_new(self):
|
||||||
|
self._test('open_new')
|
||||||
|
|
||||||
|
def test_open_new_tab(self):
|
||||||
|
self._test('open_new_tab')
|
||||||
|
|
||||||
|
|
||||||
class BrowserRegistrationTest(unittest.TestCase):
|
class BrowserRegistrationTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -314,6 +385,10 @@ class ImportTest(unittest.TestCase):
|
||||||
webbrowser.register(name, None, webbrowser.GenericBrowser(name))
|
webbrowser.register(name, None, webbrowser.GenericBrowser(name))
|
||||||
webbrowser.get(sys.executable)
|
webbrowser.get(sys.executable)
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
is_apple_mobile,
|
||||||
|
"Apple mobile doesn't allow modifying browser with environment"
|
||||||
|
)
|
||||||
def test_environment(self):
|
def test_environment(self):
|
||||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||||
try:
|
try:
|
||||||
|
@ -325,6 +400,10 @@ class ImportTest(unittest.TestCase):
|
||||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||||
webbrowser.get()
|
webbrowser.get()
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
is_apple_mobile,
|
||||||
|
"Apple mobile doesn't allow modifying browser with environment"
|
||||||
|
)
|
||||||
def test_environment_preferred(self):
|
def test_environment_preferred(self):
|
||||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -478,6 +478,9 @@ def register_standard_browsers():
|
||||||
# OS X can use below Unix support (but we prefer using the OS X
|
# OS X can use below Unix support (but we prefer using the OS X
|
||||||
# specific stuff)
|
# specific stuff)
|
||||||
|
|
||||||
|
if sys.platform == "ios":
|
||||||
|
register("iosbrowser", None, IOSBrowser(), preferred=True)
|
||||||
|
|
||||||
if sys.platform == "serenityos":
|
if sys.platform == "serenityos":
|
||||||
# SerenityOS webbrowser, simply called "Browser".
|
# SerenityOS webbrowser, simply called "Browser".
|
||||||
register("Browser", None, BackgroundBrowser("Browser"))
|
register("Browser", None, BackgroundBrowser("Browser"))
|
||||||
|
@ -599,6 +602,70 @@ if sys.platform == 'darwin':
|
||||||
rc = osapipe.close()
|
rc = osapipe.close()
|
||||||
return not rc
|
return not rc
|
||||||
|
|
||||||
|
#
|
||||||
|
# Platform support for iOS
|
||||||
|
#
|
||||||
|
if sys.platform == "ios":
|
||||||
|
from _ios_support import objc
|
||||||
|
if objc:
|
||||||
|
# If objc exists, we know ctypes is also importable.
|
||||||
|
from ctypes import c_void_p, c_char_p, c_ulong
|
||||||
|
|
||||||
|
class IOSBrowser(BaseBrowser):
|
||||||
|
def open(self, url, new=0, autoraise=True):
|
||||||
|
sys.audit("webbrowser.open", url)
|
||||||
|
# If ctypes isn't available, we can't open a browser
|
||||||
|
if objc is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# All the messages in this call return object references.
|
||||||
|
objc.objc_msgSend.restype = c_void_p
|
||||||
|
|
||||||
|
# This is the equivalent of:
|
||||||
|
# NSString url_string =
|
||||||
|
# [NSString stringWithCString:url.encode("utf-8")
|
||||||
|
# encoding:NSUTF8StringEncoding];
|
||||||
|
NSString = objc.objc_getClass(b"NSString")
|
||||||
|
constructor = objc.sel_registerName(b"stringWithCString:encoding:")
|
||||||
|
objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_char_p, c_ulong]
|
||||||
|
url_string = objc.objc_msgSend(
|
||||||
|
NSString,
|
||||||
|
constructor,
|
||||||
|
url.encode("utf-8"),
|
||||||
|
4, # NSUTF8StringEncoding = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an NSURL object representing the URL
|
||||||
|
# This is the equivalent of:
|
||||||
|
# NSURL *nsurl = [NSURL URLWithString:url];
|
||||||
|
NSURL = objc.objc_getClass(b"NSURL")
|
||||||
|
urlWithString_ = objc.sel_registerName(b"URLWithString:")
|
||||||
|
objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p]
|
||||||
|
ns_url = objc.objc_msgSend(NSURL, urlWithString_, url_string)
|
||||||
|
|
||||||
|
# Get the shared UIApplication instance
|
||||||
|
# This code is the equivalent of:
|
||||||
|
# UIApplication shared_app = [UIApplication sharedApplication]
|
||||||
|
UIApplication = objc.objc_getClass(b"UIApplication")
|
||||||
|
sharedApplication = objc.sel_registerName(b"sharedApplication")
|
||||||
|
objc.objc_msgSend.argtypes = [c_void_p, c_void_p]
|
||||||
|
shared_app = objc.objc_msgSend(UIApplication, sharedApplication)
|
||||||
|
|
||||||
|
# Open the URL on the shared application
|
||||||
|
# This code is the equivalent of:
|
||||||
|
# [shared_app openURL:ns_url
|
||||||
|
# options:NIL
|
||||||
|
# completionHandler:NIL];
|
||||||
|
openURL_ = objc.sel_registerName(b"openURL:options:completionHandler:")
|
||||||
|
objc.objc_msgSend.argtypes = [
|
||||||
|
c_void_p, c_void_p, c_void_p, c_void_p, c_void_p
|
||||||
|
]
|
||||||
|
# Method returns void
|
||||||
|
objc.objc_msgSend.restype = None
|
||||||
|
objc.objc_msgSend(shared_app, openURL_, ns_url, None, None)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
import getopt
|
import getopt
|
||||||
|
|
|
@ -186,12 +186,18 @@ PYTHONFRAMEWORKPREFIX= @PYTHONFRAMEWORKPREFIX@
|
||||||
PYTHONFRAMEWORKINSTALLDIR= @PYTHONFRAMEWORKINSTALLDIR@
|
PYTHONFRAMEWORKINSTALLDIR= @PYTHONFRAMEWORKINSTALLDIR@
|
||||||
PYTHONFRAMEWORKINSTALLNAMEPREFIX= @PYTHONFRAMEWORKINSTALLNAMEPREFIX@
|
PYTHONFRAMEWORKINSTALLNAMEPREFIX= @PYTHONFRAMEWORKINSTALLNAMEPREFIX@
|
||||||
RESSRCDIR= @RESSRCDIR@
|
RESSRCDIR= @RESSRCDIR@
|
||||||
# Deployment target selected during configure, to be checked
|
# macOS deployment target selected during configure, to be checked
|
||||||
# by distutils. The export statement is needed to ensure that the
|
# by distutils. The export statement is needed to ensure that the
|
||||||
# deployment target is active during build.
|
# deployment target is active during build.
|
||||||
MACOSX_DEPLOYMENT_TARGET=@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@
|
MACOSX_DEPLOYMENT_TARGET=@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@
|
||||||
@EXPORT_MACOSX_DEPLOYMENT_TARGET@export MACOSX_DEPLOYMENT_TARGET
|
@EXPORT_MACOSX_DEPLOYMENT_TARGET@export MACOSX_DEPLOYMENT_TARGET
|
||||||
|
|
||||||
|
# iOS Deployment target selected during configure. Unlike macOS, the iOS
|
||||||
|
# deployment target is controlled using `-mios-version-min` arguments added to
|
||||||
|
# CFLAGS and LDFLAGS by the configure script. This variable is not used during
|
||||||
|
# the build, and is only listed here so it will be included in sysconfigdata.
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET=@IPHONEOS_DEPLOYMENT_TARGET@
|
||||||
|
|
||||||
# Option to install to strip binaries
|
# Option to install to strip binaries
|
||||||
STRIPFLAG=-s
|
STRIPFLAG=-s
|
||||||
|
|
||||||
|
@ -2038,11 +2044,23 @@ testios:
|
||||||
cp -r $(srcdir)/iOS/testbed $(XCFOLDER)
|
cp -r $(srcdir)/iOS/testbed $(XCFOLDER)
|
||||||
# Copy the framework from the install location to the testbed project.
|
# Copy the framework from the install location to the testbed project.
|
||||||
cp -r $(PYTHONFRAMEWORKPREFIX)/* $(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator
|
cp -r $(PYTHONFRAMEWORKPREFIX)/* $(XCFOLDER)/Python.xcframework/ios-arm64_x86_64-simulator
|
||||||
|
|
||||||
# Run the test suite for the Xcode project, targeting the iOS simulator.
|
# Run the test suite for the Xcode project, targeting the iOS simulator.
|
||||||
# If the suite fails, extract and print the console output, then re-raise the failure
|
# If the suite fails, touch a file in the test folder as a marker
|
||||||
if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) ; then \
|
if ! xcodebuild test -project $(XCFOLDER)/iOSTestbed.xcodeproj -scheme "iOSTestbed" -destination "platform=iOS Simulator,name=iPhone SE (3rd Generation)" -resultBundlePath $(XCRESULT) ; then \
|
||||||
xcrun xcresulttool get --path $(XCRESULT) --id $$(xcrun xcresulttool get --path $(XCRESULT) --format json | $(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])"); \
|
touch $(XCFOLDER)/failed; \
|
||||||
echo ; \
|
fi
|
||||||
|
|
||||||
|
# Regardless of success or failure, extract and print the test output
|
||||||
|
xcrun xcresulttool get --path $(XCRESULT) \
|
||||||
|
--id $$( \
|
||||||
|
xcrun xcresulttool get --path $(XCRESULT) --format json | \
|
||||||
|
$(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['actions']['_values'][0]['actionResult']['logRef']['id']['_value'])" \
|
||||||
|
) \
|
||||||
|
--format json | \
|
||||||
|
$(PYTHON_FOR_BUILD) -c "import sys, json; result = json.load(sys.stdin); print(result['subsections']['_values'][1]['subsections']['_values'][0]['emittedOutput']['_value'])"
|
||||||
|
|
||||||
|
@if test -e $(XCFOLDER)/failed ; then \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -2777,8 +2795,8 @@ frameworkinstallmobileheaders: frameworkinstallunversionedstructure inclinstall
|
||||||
echo "Removing old framework headers"; \
|
echo "Removing old framework headers"; \
|
||||||
rm -rf $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; \
|
rm -rf $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers; \
|
||||||
fi
|
fi
|
||||||
mv "$(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)" "$(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers"
|
mv "$(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/include/python$(LDVERSION)" "$(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers"
|
||||||
$(LN) -fs "../$(PYTHONFRAMEWORKDIR)/Headers" "$(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/include/python$(VERSION)"
|
$(LN) -fs "../$(PYTHONFRAMEWORKDIR)/Headers" "$(DESTDIR)$(PYTHONFRAMEWORKPREFIX)/include/python$(LDVERSION)"
|
||||||
|
|
||||||
# Build the toplevel Makefile
|
# Build the toplevel Makefile
|
||||||
Makefile.pre: $(srcdir)/Makefile.pre.in config.status
|
Makefile.pre: $(srcdir)/Makefile.pre.in config.status
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Modify standard library to allow for iOS platform differences.
|
|
@ -14,6 +14,10 @@
|
||||||
#include "pycore_setobject.h" // _PySet_NextEntry()
|
#include "pycore_setobject.h" // _PySet_NextEntry()
|
||||||
#include "marshal.h" // Py_MARSHAL_VERSION
|
#include "marshal.h" // Py_MARSHAL_VERSION
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include "TargetConditionals.h"
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
module marshal
|
module marshal
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
@ -33,11 +37,14 @@ module marshal
|
||||||
* #if defined(MS_WINDOWS) && defined(_DEBUG)
|
* #if defined(MS_WINDOWS) && defined(_DEBUG)
|
||||||
*/
|
*/
|
||||||
#if defined(MS_WINDOWS)
|
#if defined(MS_WINDOWS)
|
||||||
#define MAX_MARSHAL_STACK_DEPTH 1000
|
# define MAX_MARSHAL_STACK_DEPTH 1000
|
||||||
#elif defined(__wasi__)
|
#elif defined(__wasi__)
|
||||||
#define MAX_MARSHAL_STACK_DEPTH 1500
|
# define MAX_MARSHAL_STACK_DEPTH 1500
|
||||||
|
// TARGET_OS_IPHONE covers any non-macOS Apple platform.
|
||||||
|
#elif defined(__APPLE__) && TARGET_OS_IPHONE
|
||||||
|
# define MAX_MARSHAL_STACK_DEPTH 1500
|
||||||
#else
|
#else
|
||||||
#define MAX_MARSHAL_STACK_DEPTH 2000
|
# define MAX_MARSHAL_STACK_DEPTH 2000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TYPE_NULL '0'
|
#define TYPE_NULL '0'
|
||||||
|
|
|
@ -38,6 +38,7 @@ static const char* _Py_stdlib_module_names[] = {
|
||||||
"_heapq",
|
"_heapq",
|
||||||
"_imp",
|
"_imp",
|
||||||
"_io",
|
"_io",
|
||||||
|
"_ios_support",
|
||||||
"_json",
|
"_json",
|
||||||
"_locale",
|
"_locale",
|
||||||
"_lsprof",
|
"_lsprof",
|
||||||
|
|
|
@ -976,7 +976,7 @@ LDFLAGS
|
||||||
CFLAGS
|
CFLAGS
|
||||||
CC
|
CC
|
||||||
HAS_XCRUN
|
HAS_XCRUN
|
||||||
IOS_DEPLOYMENT_TARGET
|
IPHONEOS_DEPLOYMENT_TARGET
|
||||||
EXPORT_MACOSX_DEPLOYMENT_TARGET
|
EXPORT_MACOSX_DEPLOYMENT_TARGET
|
||||||
CONFIGURE_MACOSX_DEPLOYMENT_TARGET
|
CONFIGURE_MACOSX_DEPLOYMENT_TARGET
|
||||||
_PYTHON_HOST_PLATFORM
|
_PYTHON_HOST_PLATFORM
|
||||||
|
@ -4442,15 +4442,16 @@ if test "$cross_compiling" = yes; then
|
||||||
_host_device=`echo $host | cut -d '-' -f4`
|
_host_device=`echo $host | cut -d '-' -f4`
|
||||||
_host_device=${_host_device:=os}
|
_host_device=${_host_device:=os}
|
||||||
|
|
||||||
IOS_DEPLOYMENT_TARGET=${_host_os:3}
|
# IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version
|
||||||
IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:=12.0}
|
IPHONEOS_DEPLOYMENT_TARGET=${_host_os:3}
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=12.0}
|
||||||
|
|
||||||
case "$host_cpu" in
|
case "$host_cpu" in
|
||||||
aarch64)
|
aarch64)
|
||||||
_host_ident=${IOS_DEPLOYMENT_TARGET}-arm64-iphone${_host_device}
|
_host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphone${_host_device}
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_host_ident=${IOS_DEPLOYMENT_TARGET}-$host_cpu-iphone${_host_device}
|
_host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphone${_host_device}
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
@ -4597,6 +4598,9 @@ fi
|
||||||
CONFIGURE_MACOSX_DEPLOYMENT_TARGET=
|
CONFIGURE_MACOSX_DEPLOYMENT_TARGET=
|
||||||
EXPORT_MACOSX_DEPLOYMENT_TARGET='#'
|
EXPORT_MACOSX_DEPLOYMENT_TARGET='#'
|
||||||
|
|
||||||
|
# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple.
|
||||||
|
|
||||||
|
|
||||||
# checks for alternative programs
|
# checks for alternative programs
|
||||||
|
|
||||||
# compiler flags are generated in two sets, BASECFLAGS and OPT. OPT is just
|
# compiler flags are generated in two sets, BASECFLAGS and OPT. OPT is just
|
||||||
|
@ -4632,9 +4636,8 @@ esac
|
||||||
case $ac_sys_system in #(
|
case $ac_sys_system in #(
|
||||||
iOS) :
|
iOS) :
|
||||||
|
|
||||||
as_fn_append CFLAGS " -mios-version-min=${IOS_DEPLOYMENT_TARGET}"
|
as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"
|
||||||
as_fn_append LDFLAGS " -mios-version-min=${IOS_DEPLOYMENT_TARGET}"
|
as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"
|
||||||
|
|
||||||
;; #(
|
;; #(
|
||||||
*) :
|
*) :
|
||||||
;;
|
;;
|
||||||
|
@ -27497,6 +27500,8 @@ else $as_nop
|
||||||
with_ensurepip=no ;; #(
|
with_ensurepip=no ;; #(
|
||||||
WASI) :
|
WASI) :
|
||||||
with_ensurepip=no ;; #(
|
with_ensurepip=no ;; #(
|
||||||
|
iOS) :
|
||||||
|
with_ensurepip=no ;; #(
|
||||||
*) :
|
*) :
|
||||||
with_ensurepip=upgrade
|
with_ensurepip=upgrade
|
||||||
;;
|
;;
|
||||||
|
|
19
configure.ac
19
configure.ac
|
@ -715,16 +715,16 @@ if test "$cross_compiling" = yes; then
|
||||||
_host_device=`echo $host | cut -d '-' -f4`
|
_host_device=`echo $host | cut -d '-' -f4`
|
||||||
_host_device=${_host_device:=os}
|
_host_device=${_host_device:=os}
|
||||||
|
|
||||||
dnl IOS_DEPLOYMENT_TARGET is the minimum supported iOS version
|
# IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version
|
||||||
IOS_DEPLOYMENT_TARGET=${_host_os:3}
|
IPHONEOS_DEPLOYMENT_TARGET=${_host_os:3}
|
||||||
IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:=12.0}
|
IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=12.0}
|
||||||
|
|
||||||
case "$host_cpu" in
|
case "$host_cpu" in
|
||||||
aarch64)
|
aarch64)
|
||||||
_host_ident=${IOS_DEPLOYMENT_TARGET}-arm64-iphone${_host_device}
|
_host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphone${_host_device}
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_host_ident=${IOS_DEPLOYMENT_TARGET}-$host_cpu-iphone${_host_device}
|
_host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphone${_host_device}
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
@ -866,6 +866,9 @@ AC_SUBST([EXPORT_MACOSX_DEPLOYMENT_TARGET])
|
||||||
CONFIGURE_MACOSX_DEPLOYMENT_TARGET=
|
CONFIGURE_MACOSX_DEPLOYMENT_TARGET=
|
||||||
EXPORT_MACOSX_DEPLOYMENT_TARGET='#'
|
EXPORT_MACOSX_DEPLOYMENT_TARGET='#'
|
||||||
|
|
||||||
|
# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple.
|
||||||
|
AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET])
|
||||||
|
|
||||||
# checks for alternative programs
|
# checks for alternative programs
|
||||||
|
|
||||||
# compiler flags are generated in two sets, BASECFLAGS and OPT. OPT is just
|
# compiler flags are generated in two sets, BASECFLAGS and OPT. OPT is just
|
||||||
|
@ -901,9 +904,8 @@ AS_CASE([$host],
|
||||||
dnl Add the compiler flag for the iOS minimum supported OS version.
|
dnl Add the compiler flag for the iOS minimum supported OS version.
|
||||||
AS_CASE([$ac_sys_system],
|
AS_CASE([$ac_sys_system],
|
||||||
[iOS], [
|
[iOS], [
|
||||||
AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IOS_DEPLOYMENT_TARGET}"])
|
AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
|
||||||
AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IOS_DEPLOYMENT_TARGET}"])
|
AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
|
||||||
AC_SUBST([IOS_DEPLOYMENT_TARGET])
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6939,6 +6941,7 @@ AC_ARG_WITH([ensurepip],
|
||||||
AS_CASE([$ac_sys_system],
|
AS_CASE([$ac_sys_system],
|
||||||
[Emscripten], [with_ensurepip=no],
|
[Emscripten], [with_ensurepip=no],
|
||||||
[WASI], [with_ensurepip=no],
|
[WASI], [with_ensurepip=no],
|
||||||
|
[iOS], [with_ensurepip=no],
|
||||||
[with_ensurepip=upgrade]
|
[with_ensurepip=upgrade]
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
|
|
|
@ -29,6 +29,6 @@
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>@IOS_DEPLOYMENT_TARGET@</string>
|
<string>@IPHONEOS_DEPLOYMENT_TARGET@</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -441,7 +441,7 @@
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = 3HEZE76D99;
|
DEVELOPMENT_TEAM = "";
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
|
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
|
||||||
INFOPLIST_FILE = "iOSTestbed/iOSTestbed-Info.plist";
|
INFOPLIST_FILE = "iOSTestbed/iOSTestbed-Info.plist";
|
||||||
|
@ -471,7 +471,7 @@
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = 3HEZE76D99;
|
DEVELOPMENT_TEAM = "";
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
|
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
|
||||||
|
|
Loading…
Reference in New Issue