diff --git a/.gitignore b/.gitignore
index 7965f6a404368e..4c5221f4f62f0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -71,6 +71,7 @@ Lib/test/data/*
/Makefile
/Makefile.pre
/iOSTestbed.*
+/visionOSTestbed.*
iOS/Frameworks/
iOS/Resources/Info.plist
iOS/testbed/build
@@ -81,6 +82,20 @@ iOS/testbed/Python.xcframework/ios-*/Python.framework
iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace
iOS/testbed/iOSTestbed.xcodeproj/xcuserdata
iOS/testbed/iOSTestbed.xcodeproj/xcshareddata
+MacCatalyst/Resources/Info.plist
+visionOS/testbed/Python.xcframework/xros-*/bin
+visionOS/testbed/Python.xcframework/xros-*/include
+visionOS/testbed/Python.xcframework/xros-*/lib
+visionOS/testbed/Python.xcframework/xros-*/Python.framework
+visionOS/testbed/visionOSTestbed.xcodeproj/project.xcworkspace
+visionOS/testbed/visionOSTestbed.xcodeproj/xcuserdata
+visionOS/testbed/visionOSTestbed.xcodeproj/xcshareddata
+tvOS/Frameworks
+tvOS/Resources/Info.plist
+watchOS/Frameworks
+watchOS/Resources/Info.plist
+visionOS/Frameworks
+visionOS/Resources/Info.plist
Mac/Makefile
Mac/PythonLauncher/Info.plist
Mac/PythonLauncher/Makefile
diff --git a/Lib/_ios_support.py b/Lib/_ios_support.py
index 20467a7c2bcaeb..deefacaf8c5234 100644
--- a/Lib/_ios_support.py
+++ b/Lib/_ios_support.py
@@ -25,6 +25,7 @@
def get_platform_ios():
# Determine if this is a simulator using the multiarch value
is_simulator = sys.implementation._multiarch.endswith("simulator")
+ is_catalyst = sys.implementation._multiarch.endswith("macabi")
# We can't use ctypes; abort
if not objc:
@@ -68,4 +69,4 @@ def get_platform_ios():
release = objc.objc_msgSend(device_systemVersion, SEL_UTF8String).decode()
model = objc.objc_msgSend(device_model, SEL_UTF8String).decode()
- return system, release, model, is_simulator
+ return system, release, model, is_simulator, is_catalyst
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index 823a3692fd1bbf..00639dd8488267 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -419,7 +419,7 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None,
if name:
name = _os.fspath(name)
- # If the filename that has been provided is an iOS/tvOS/watchOS
+ # If the filename that has been provided is an iOS/tvOS/watchOS/visionOS
# .fwork file, dereference the location to the true origin of the
# binary.
if name.endswith(".fwork"):
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
index 99504911a3dbe0..527c2f36dd07f2 100644
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -126,7 +126,7 @@ def dllist():
if (name := _get_module_filename(h)) is not None]
return libraries
-elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos"}:
+elif os.name == "posix" and sys.platform in {"darwin", "ios", "tvos", "watchos", "visionos"}:
from ctypes.macholib.dyld import dyld_find as _dyld_find
def find_library(name):
possible = ['lib%s.dylib' % name,
@@ -425,7 +425,7 @@ def find_library(name):
# https://man.openbsd.org/dl_iterate_phdr
# https://docs.oracle.com/cd/E88353_01/html/E37843/dl-iterate-phdr-3c.html
if (os.name == "posix" and
- sys.platform not in {"darwin", "ios", "tvos", "watchos"}):
+ sys.platform not in {"darwin", "ios", "tvos", "watchos", "visionos"}):
import ctypes
if hasattr((_libc := ctypes.CDLL(None)), "dl_iterate_phdr"):
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index 8bcd741c446bd2..dfab36488f6943 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', 'ios', 'tvos', 'watchos'
+_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY = 'cygwin', 'darwin', 'ios', 'tvos', 'watchos', 'visionos'
_CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY
+ _CASE_INSENSITIVE_PLATFORMS_STR_KEY)
@@ -1535,7 +1535,8 @@ def _get_supported_file_loaders():
"""
extension_loaders = []
if hasattr(_imp, 'create_dynamic'):
- if sys.platform in {"ios", "tvos", "watchos"}:
+ # AppleFrameworkLoader is unessaccary on Mac Catalyst because Mac Catalyst allows "raw" dylib files.
+ if sys.platform in {"ios", "tvos", "watchos", "visionos"} and not sys.implementation._multiarch.endswith("macabi"):
extension_loaders = [(AppleFrameworkLoader, [
suffix.replace(".so", ".fwork")
for suffix in _imp.extension_suffixes()
diff --git a/Lib/platform.py b/Lib/platform.py
index 55e211212d4209..37dd6ce7bf1595 100644
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -508,11 +508,11 @@ def mac_ver(release='', versioninfo=('', '', ''), machine=''):
# A namedtuple for iOS version information.
IOSVersionInfo = collections.namedtuple(
"IOSVersionInfo",
- ["system", "release", "model", "is_simulator"]
+ ["system", "release", "model", "is_simulator", "is_catalyst"]
)
-def ios_ver(system="", release="", model="", is_simulator=False):
+def ios_ver(system="", release="", model="", is_simulator=False, is_catalyst=False):
"""Get iOS version information, and return it as a namedtuple:
(system, release, model, is_simulator).
@@ -525,7 +525,82 @@ def ios_ver(system="", release="", model="", is_simulator=False):
if result is not None:
return IOSVersionInfo(*result)
- return IOSVersionInfo(system, release, model, is_simulator)
+ return IOSVersionInfo(system, release, model, is_simulator, is_catalyst)
+
+
+# A namedtuple for tvOS version information.
+TVOSVersionInfo = collections.namedtuple(
+ "TVOSVersionInfo",
+ ["system", "release", "model", "is_simulator"]
+)
+
+
+def tvos_ver(system="", release="", model="", is_simulator=False):
+ """Get tvOS 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 == "tvos":
+ # TODO: Can the iOS implementation be used here?
+ import _ios_support
+ result = _ios_support.get_platform_ios()
+ if result is not None:
+ result = result[:-1] # ignore the Catalyst flag
+ return TVOSVersionInfo(*result)
+
+ return TVOSVersionInfo(system, release, model, is_simulator)
+
+
+# A namedtuple for watchOS version information.
+WatchOSVersionInfo = collections.namedtuple(
+ "WatchOSVersionInfo",
+ ["system", "release", "model", "is_simulator"]
+)
+
+
+def watchos_ver(system="", release="", model="", is_simulator=False):
+ """Get watchOS 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 == "watchos":
+ # TODO: Can the iOS implementation be used here?
+ import _ios_support
+ result = _ios_support.get_platform_ios()
+ if result is not None:
+ result = result[:-1] # ignore the Catalyst flag
+ return WatchOSVersionInfo(*result)
+
+ return WatchOSVersionInfo(system, release, model, is_simulator)
+
+
+# A namedtuple for visionOS version information.
+VisionOSVersionInfo = collections.namedtuple(
+ "VisionOSVersionInfo",
+ ["system", "release", "model", "is_simulator"]
+)
+
+
+def visionos_ver(system="", release="", model="", is_simulator=False):
+ """Get visionOS 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 == "visionos":
+ # TODO: Can the iOS implementation be used here?
+ import _ios_support
+ result = _ios_support.get_platform_ios()
+ if result is not None:
+ result = result[:-1] # ignore the Catalyst flag
+ return VisionOSVersionInfo(*result)
+
+ return VisionOSVersionInfo(system, release, model, is_simulator)
def _java_getprop(name, default):
@@ -727,7 +802,7 @@ def _syscmd_file(target, default=''):
default in case the command should fail.
"""
- if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}:
+ if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos', 'visionos'}:
# XXX Others too ?
return default
@@ -891,10 +966,26 @@ def get_OpenVMS():
csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
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.
+ # On the iOS/tvOS/watchOS/visionOS simulator and Mac Catalyst, 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 devices, so we know the
+ # right answer.
def get_ios():
+ if sys.implementation._multiarch.endswith("simulator") or sys.implementation._multiarch.endswith("macabi"):
+ return os.uname().machine
+ return 'arm64'
+
+ def get_tvos():
+ if sys.implementation._multiarch.endswith("simulator"):
+ return os.uname().machine
+ return 'arm64'
+
+ def get_watchos():
+ if sys.implementation._multiarch.endswith("simulator"):
+ return os.uname().machine
+ return 'arm64_32'
+
+ def get_visionos():
if sys.implementation._multiarch.endswith("simulator"):
return os.uname().machine
return 'arm64'
@@ -1058,9 +1149,15 @@ def uname():
system = 'Android'
release = android_ver().release
- # Normalize responses on iOS
+ # Normalize responses on Apple mobile platforms
if sys.platform == 'ios':
- system, release, _, _ = ios_ver()
+ system, release, _, _, _ = ios_ver()
+ if sys.platform == 'tvos':
+ system, release, _, _ = tvos_ver()
+ if sys.platform == 'watchos':
+ system, release, _, _ = watchos_ver()
+ if sys.platform == 'visionos':
+ system, release, _, _ = visionos_ver()
vals = system, node, release, version, machine
# Replace 'unknown' values with the more portable ''
@@ -1347,9 +1444,15 @@ def platform(aliased=False, terse=False):
system, release, version = system_alias(system, release, version)
if system == 'Darwin':
- # macOS and iOS both report as a "Darwin" kernel
+ # All Apple Platforms report as a "Darwin" kernel
if sys.platform == "ios":
- system, release, _, _ = ios_ver()
+ system, release, _, _, _ = ios_ver()
+ elif sys.platform == "tvos":
+ system, release, _, _ = tvos_ver()
+ elif sys.platform == "watchos":
+ system, release, _, _ = watchos_ver()
+ elif sys.platform == "visionos":
+ system, release, _, _ = visionos_ver()
else:
macos_release = mac_ver()[0]
if macos_release:
diff --git a/Lib/site.py b/Lib/site.py
index f93271971594d8..74899abecb0aa7 100644
--- a/Lib/site.py
+++ b/Lib/site.py
@@ -298,8 +298,8 @@ def _getuserbase():
if env_base:
return env_base
- # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories
- if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
+ # Emscripten, iOS, tvOS, visionOS, VxWorks, WASI, and watchOS have no home directories
+ if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "visionos", "wasi", "watchos"}:
return None
def joinuser(*args):
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 54c2eb515b60da..9b3f9f612e9581 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -75,7 +75,10 @@
_mswindows = True
# some platforms do not support subprocesses
-_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos"}
+_can_fork_exec = (
+ sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"}
+ or sys.implementation._multiarch.endswith("macabi") # Mac Catalyst
+)
if _mswindows:
import _winapi
diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py
index f93b98dd681536..0db3dbdce050ff 100644
--- a/Lib/sysconfig/__init__.py
+++ b/Lib/sysconfig/__init__.py
@@ -23,6 +23,9 @@
_ALWAYS_STR = {
'IPHONEOS_DEPLOYMENT_TARGET',
'MACOSX_DEPLOYMENT_TARGET',
+ 'TVOS_DEPLOYMENT_TARGET',
+ 'WATCHOS_DEPLOYMENT_TARGET',
+ 'XROS_DEPLOYMENT_TARGET',
}
_INSTALL_SCHEMES = {
@@ -119,7 +122,7 @@ def _getuserbase():
# Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories.
# Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling.
system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0]
- if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:
+ if system_name in {"emscripten", "ios", "tvos", "visionos", "vxworks", "wasi", "watchos"}:
return None
def joinuser(*args):
@@ -730,6 +733,18 @@ def get_platform():
release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0")
osname = sys.platform
machine = sys.implementation._multiarch
+ elif sys.platform == "tvos":
+ release = get_config_vars().get("TVOS_DEPLOYMENT_TARGET", "9.0")
+ osname = sys.platform
+ machine = sys.implementation._multiarch
+ elif sys.platform == "watchos":
+ release = get_config_vars().get("WATCHOS_DEPLOYMENT_TARGET", "4.0")
+ osname = sys.platform
+ machine = sys.implementation._multiarch
+ elif sys.platform == "visionos":
+ release = get_config_vars().get("XROS_DEPLOYMENT_TARGET", "2.0")
+ osname = sys.platform
+ machine = sys.implementation._multiarch
else:
import _osx_support
osname, release, machine = _osx_support.get_platform_osx(
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 1b551254f86c32..7eecf926082995 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -7159,9 +7159,9 @@ def test_datetime_from_timestamp(self):
self.assertEqual(dt_orig, dt_rt)
def test_type_check_in_subinterp(self):
- # iOS requires the use of the custom framework loader,
- # not the ExtensionFileLoader.
- if sys.platform == "ios":
+ # Apple mobile platforms except Mac Catalyst require the use of the
+ # custom framework loader, not the ExtensionFileLoader.
+ if support.needs_apple_fworks:
extension_loader = "AppleFrameworkLoader"
else:
extension_loader = "ExtensionFileLoader"
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index b7cd7940eb15b3..4f027912b0a5a6 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -46,6 +46,7 @@
# sys
"MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
"is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
+ "is_mac_catalyst", "needs_apple_fworks",
# os
"get_pagesize",
# network
@@ -558,7 +559,7 @@ def skip_android_selinux(name):
sys.platform == "android", f"Android blocks {name} with SELinux"
)
-if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos"}:
+if sys.platform not in {"win32", "vxworks", "ios", "tvos", "watchos", "visionos"}:
unix_shell = '/system/bin/sh' if is_android else '/bin/sh'
else:
unix_shell = None
@@ -574,8 +575,10 @@ def skip_emscripten_stack_overflow():
def skip_wasi_stack_overflow():
return unittest.skipIf(is_wasi, "Exhausts stack on WASI")
-is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"}
+is_apple_mobile = sys.platform in {"ios", "tvos", "watchos", "visionos"}
is_apple = is_apple_mobile or sys.platform == "darwin"
+is_mac_catalyst = sys.implementation._multiarch.endswith("macabi")
+needs_apple_fworks = is_apple_mobile and not is_mac_catalyst
has_fork_support = hasattr(os, "fork") and not (
# WASM and Apple mobile platforms do not support subprocesses.
@@ -586,6 +589,9 @@ def skip_wasi_stack_overflow():
# Although Android supports fork, it's unsafe to call it from Python because
# all Android apps are multi-threaded.
or is_android
+
+ # Mac Catalyst supports subprocesses.
+ and not is_mac_catalyst
)
def requires_fork():
@@ -601,6 +607,9 @@ def requires_fork():
# practice (see PEP 738). And most of the tests that use them are calling
# sys.executable, which won't work when Python is embedded in an Android app.
or is_android
+
+ # Mac Catalyst supports subprocesses.
+ and not is_mac_catalyst
)
def requires_subprocess():
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index f74694a7a745a4..5ad6f4d4ff1fc5 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -1920,7 +1920,7 @@ def test_module_state_shared_in_global(self):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if support.is_apple_mobile:
+ if support.needs_apple_fworks:
loader = "AppleFrameworkLoader"
else:
loader = "ExtensionFileLoader"
@@ -2604,7 +2604,7 @@ def setUp(self):
origin = importlib.util.find_spec('_testmultiphase').origin
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if support.is_apple_mobile:
+ if support.needs_apple_fworks:
loader = importlib.machinery.AppleFrameworkLoader(fullname, origin)
else:
loader = importlib.machinery.ExtensionFileLoader(fullname, origin)
diff --git a/Lib/test/test_ctypes/test_dllist.py b/Lib/test/test_ctypes/test_dllist.py
index 15603dc3d77972..bff6c0fb95f129 100644
--- a/Lib/test/test_ctypes/test_dllist.py
+++ b/Lib/test/test_ctypes/test_dllist.py
@@ -7,7 +7,7 @@
WINDOWS = os.name == "nt"
-APPLE = sys.platform in {"darwin", "ios", "tvos", "watchos"}
+APPLE = sys.platform in {"darwin", "ios", "tvos", "watchos", "visionos"}
if WINDOWS:
KNOWN_LIBRARIES = ["KERNEL32.DLL"]
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 6e34094c5aa422..87a81fdecfd927 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -33,7 +33,7 @@
swap_attr,
swap_item,
cpython_only,
- is_apple_mobile,
+ needs_apple_fworks,
is_emscripten,
is_wasi,
run_in_subinterp,
@@ -111,7 +111,7 @@ def require_builtin(module, *, skip=False):
def require_extension(module, *, skip=False):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if is_apple_mobile:
+ if needs_apple_fworks:
_require_loader(module, AppleFrameworkLoader, skip)
else:
_require_loader(module, ExtensionFileLoader, skip)
@@ -126,7 +126,7 @@ def require_pure_python(module, *, skip=False):
def create_extension_loader(modname, filename):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if is_apple_mobile:
+ if needs_apple_fworks:
return AppleFrameworkLoader(modname, filename)
else:
return ExtensionFileLoader(modname, filename)
@@ -2217,7 +2217,7 @@ def import_script(self, name, fd, filename=None, check_override=None):
if filename:
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if is_apple_mobile:
+ if needs_apple_fworks:
loader = "AppleFrameworkLoader"
else:
loader = "ExtensionFileLoader"
@@ -2692,7 +2692,7 @@ def setUpClass(cls):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader, and we need to differentiate between the
# spec.origin and the original file location.
- if is_apple_mobile:
+ if needs_apple_fworks:
assert cls.LOADER is AppleFrameworkLoader
cls.ORIGIN = spec.origin
diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py
index cdc8884d668a66..d1b525f6419791 100644
--- a/Lib/test/test_importlib/extension/test_finder.py
+++ b/Lib/test/test_importlib/extension/test_finder.py
@@ -1,4 +1,4 @@
-from test.support import is_apple_mobile
+from test.support import needs_apple_fworks
from test.test_importlib import abc, util
machinery = util.import_importlib('importlib.machinery')
@@ -20,7 +20,7 @@ def setUp(self):
)
def find_spec(self, fullname):
- if is_apple_mobile:
+ if needs_apple_fworks:
# Apple mobile platforms require a specialist loader that uses
# .fwork files as placeholders for the true `.so` files.
loaders = [
diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py
index 0dd21e079eba22..efae18d4bbd5c5 100644
--- a/Lib/test/test_importlib/extension/test_loader.py
+++ b/Lib/test/test_importlib/extension/test_loader.py
@@ -1,4 +1,4 @@
-from test.support import is_apple_mobile
+from test.support import needs_apple_fworks
from test.test_importlib import abc, util
machinery = util.import_importlib('importlib.machinery')
@@ -28,7 +28,7 @@ def setUp(self):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if is_apple_mobile:
+ if needs_apple_fworks:
self.LoaderClass = self.machinery.AppleFrameworkLoader
else:
self.LoaderClass = self.machinery.ExtensionFileLoader
@@ -110,7 +110,7 @@ def setUp(self):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if is_apple_mobile:
+ if needs_apple_fworks:
self.LoaderClass = self.machinery.AppleFrameworkLoader
else:
self.LoaderClass = self.machinery.ExtensionFileLoader
@@ -198,7 +198,7 @@ def setUp(self):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if is_apple_mobile:
+ if needs_apple_fworks:
self.LoaderClass = self.machinery.AppleFrameworkLoader
else:
self.LoaderClass = self.machinery.ExtensionFileLoader
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index 5de89714eb50c7..0c6661791cabcc 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -714,7 +714,7 @@ def test_single_phase_init_module(self):
def test_incomplete_multi_phase_init_module(self):
# Apple extensions must be distributed as frameworks. This requires
# a specialist loader.
- if support.is_apple_mobile:
+ if support.needs_apple_fworks:
loader = "AppleFrameworkLoader"
else:
loader = "ExtensionFileLoader"
diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py
index edbe78545a2536..d1f6965196fd3a 100644
--- a/Lib/test/test_importlib/util.py
+++ b/Lib/test/test_importlib/util.py
@@ -8,7 +8,7 @@
import os.path
from test import support
from test.support import import_helper
-from test.support import is_apple_mobile
+from test.support import needs_apple_fworks
from test.support import os_helper
import unittest
import sys
@@ -48,7 +48,7 @@ def _extension_details():
for ext in machinery.EXTENSION_SUFFIXES:
# Apple mobile platforms mechanically load .so files,
# but the findable files are labelled .fwork
- if is_apple_mobile:
+ if needs_apple_fworks:
ext = ext.replace(".so", ".fwork")
filename = EXTENSIONS.name + ext
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 88b5b0e6e358bb..c5121fee00e761 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -2510,19 +2510,54 @@ def test_isatty(self):
@unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()')
def test_closerange(self):
- fd = os_helper.make_bad_fd()
- # Make sure none of the descriptors we are about to close are
- # currently valid (issue 6542).
- for i in range(10):
- try: os.fstat(fd+i)
- except OSError:
- pass
- else:
- break
- if i < 2:
- raise unittest.SkipTest(
- "Unable to acquire a range of invalid file descriptors")
- self.assertEqual(os.closerange(fd, fd + i-1), None)
+ if support.is_mac_catalyst:
+ # On Mac Catalyst and potentially other Apple platforms,
+ # somehow there's a guarded FD in the way, that has no
+ # effect on fstat. We make some random FDs first, stop
+ # once not consecutive, close all of them one by one by calling
+ # file.close(), then assert that closerange on the FDs is failing.
+ # This ensures that none of those things we're closing is
+ # guarded, if we're careful to not use code that makes guarded
+ # file descriptors between when we manually made and
+ # destroyed them and when we're attempting to invoke
+ # closerange.
+
+ copies = []
+ # Open a file for testing and get its FD
+ file = open(os_helper.TESTFN, "wb")
+ fd = file.fileno()
+ copies.append(file)
+ for i in range(1, 10):
+ file_dup = open(os_helper.TESTFN, "wb")
+ if file_dup.fileno() != fd + i:
+ file_dup.close()
+ break
+ else:
+ copies.append(file_dup)
+ # Close everything
+ for copy in copies:
+ copy.close()
+ os.unlink(os_helper.TESTFN)
+ if i < 2:
+ raise unittest.SkipTest(
+ "Unable to acquire a range of invalid file descriptors")
+
+ # Now we're left with invalid FDs. Let's go close them!
+ self.assertEqual(os.closerange(fd, fd + i-1), None)
+ else:
+ fd = os_helper.make_bad_fd()
+ # Make sure none of the descriptors we are about to close are
+ # currently valid (issue 6542).
+ for i in range(10):
+ try: os.fstat(fd+i)
+ except OSError:
+ pass
+ else:
+ break
+ if i < 2:
+ raise unittest.SkipTest(
+ "Unable to acquire a range of invalid file descriptors")
+ self.assertEqual(os.closerange(fd, fd + i-1), None)
@unittest.skipUnless(hasattr(os, 'dup2'), 'test needs os.dup2()')
def test_dup2(self):
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
index 719c4feace6544..2f4f221fe4faed 100644
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -271,13 +271,21 @@ def test_uname(self):
if sys.platform == "android":
self.assertEqual(res.system, "Android")
self.assertEqual(res.release, platform.android_ver().release)
- elif sys.platform == "ios":
+ elif support.is_apple_mobile:
# 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)
+ if sys.platform == "ios":
+ # iPads also identify as iOS
+ self.assertIn(res.system, {"iOS", "iPadOS"})
+ else:
+ # All other platforms - sys.platform is the lower case
+ # form of system (e.g., visionOS->visionos)
+ self.assertEqual(res.system.lower(), sys.platform)
+ # Use the platform-specific version method
+ platform_ver = getattr(platform, f"{sys.platform}_ver")
+ self.assertEqual(res.release, platform_ver().release)
else:
self.assertEqual(res.system, "")
self.assertEqual(res.release, "")
@@ -480,7 +488,7 @@ def test_ios_ver(self):
# ios_ver is only fully available on iOS where ctypes is available.
if sys.platform == "ios" and _ctypes:
- system, release, model, is_simulator = result
+ system, release, model, is_simulator, is_catalyst = result
# Result is a namedtuple
self.assertEqual(result.system, system)
self.assertEqual(result.release, release)
@@ -491,6 +499,7 @@ def test_ios_ver(self):
# ios_ver(), so we check that the values are broadly what we expect.
# System is either iOS or iPadOS, depending on the test device
+ # Mac Catalyst returns iPadOS.
self.assertIn(system, {"iOS", "iPadOS"})
# Release is a numeric version specifier with at least 2 parts
@@ -503,6 +512,9 @@ def test_ios_ver(self):
# we get a model descriptor like "iPhone13,1"
if is_simulator:
self.assertIn(model, {"iPhone", "iPad"})
+ # Mac Catalyst identifies as iPad with no version.
+ elif is_catalyst:
+ self.assertEqual(model, "iPad")
else:
self.assertTrue(
(model.startswith("iPhone") or model.startswith("iPad"))
@@ -510,6 +522,10 @@ def test_ios_ver(self):
)
self.assertEqual(type(is_simulator), bool)
+
+ # Mac Catalyst platform will return iPadOS.
+ if is_catalyst:
+ self.assertEqual(system, "iPadOS")
else:
# On non-iOS platforms, calling ios_ver doesn't fail; you get
# default values
@@ -519,11 +535,12 @@ def test_ios_ver(self):
self.assertFalse(result.is_simulator)
# Check the fallback values can be overridden by arguments
- override = platform.ios_ver("Foo", "Bar", "Whiz", True)
+ override = platform.ios_ver("Foo", "Bar", "Whiz", True, True)
self.assertEqual(override.system, "Foo")
self.assertEqual(override.release, "Bar")
self.assertEqual(override.model, "Whiz")
self.assertTrue(override.is_simulator)
+ self.assertTrue(override.is_catalyst)
@unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten")
def test_libc_ver(self):
diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py
index 4c3ea1cd8df13e..04a210e5c86848 100644
--- a/Lib/test/test_webbrowser.py
+++ b/Lib/test/test_webbrowser.py
@@ -236,7 +236,8 @@ def test_open_new_tab(self):
arguments=[f'openURL({URL},new-tab)'])
-@unittest.skipUnless(sys.platform == "ios", "Test only applicable to iOS")
+@unittest.skipUnless(sys.platform in {"ios", "visionOS"},
+ "Test only applicable to iOS and visionOS")
class IOSBrowserTest(unittest.TestCase):
def _obj_ref(self, *args):
# Construct a string representation of the arguments that can be used
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index f2e2394089d5a1..2efbbfb0014736 100644
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -488,7 +488,8 @@ def register_standard_browsers():
# macOS can use below Unix support (but we prefer using the macOS
# specific stuff)
- if sys.platform == "ios":
+ if sys.platform in {"ios", "visionos"}:
+ # iOS and visionOS provide a browser; tvOS and watchOS don't.
register("iosbrowser", None, IOSBrowser(), preferred=True)
if sys.platform == "serenityos":
@@ -653,9 +654,10 @@ def open(self, url, new=0, autoraise=True):
return not rc
#
-# Platform support for iOS
+# Platform support for Apple Mobile platforms that provide a browser
+# (i.e., iOS and visionOS)
#
-if sys.platform == "ios":
+if sys.platform in {"ios", "visionos"}:
from _ios_support import objc
if objc:
# If objc exists, we know ctypes is also importable.
diff --git a/MacCatalyst/Resources/Info.plist.in b/MacCatalyst/Resources/Info.plist.in
new file mode 100644
index 00000000000000..80b5664508ec55
--- /dev/null
+++ b/MacCatalyst/Resources/Info.plist.in
@@ -0,0 +1,38 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ Python
+ CFBundleGetInfoString
+ Python Runtime and Library
+ CFBundleIdentifier
+ @PYTHONFRAMEWORKIDENTIFIER@
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Python
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ %VERSION%
+ CFBundleLongVersionString
+ %VERSION%, (c) 2001-2024 Python Software Foundation.
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ %VERSION%
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ LSMinimumSystemVersion
+ @CATALYST_MACOS_VERSION@
+ UIDeviceFamily
+
+ 2
+
+
+
diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar
new file mode 100755
index 00000000000000..1e31b940c23c17
--- /dev/null
+++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} ar "$@"
diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang
new file mode 100755
index 00000000000000..6845b5d1b20c8d
--- /dev/null
+++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@"
diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++
new file mode 100755
index 00000000000000..779c3166268b08
--- /dev/null
+++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@"
diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp
new file mode 100755
index 00000000000000..01ff56a0f0ed03
--- /dev/null
+++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi -E "$@"
diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar
new file mode 100755
index 00000000000000..1e31b940c23c17
--- /dev/null
+++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} ar "$@"
diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang
new file mode 100755
index 00000000000000..a3170bb1f1553c
--- /dev/null
+++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@"
diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++
new file mode 100755
index 00000000000000..d3161b335e1605
--- /dev/null
+++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@"
diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp
new file mode 100755
index 00000000000000..7e3e9124123e62
--- /dev/null
+++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp
@@ -0,0 +1,2 @@
+#!/bin/sh
+xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi -E "$@"
diff --git a/MacCatalyst/Resources/pyconfig.h b/MacCatalyst/Resources/pyconfig.h
new file mode 100644
index 00000000000000..4acff2c6051637
--- /dev/null
+++ b/MacCatalyst/Resources/pyconfig.h
@@ -0,0 +1,7 @@
+#ifdef __arm64__
+#include "pyconfig-arm64.h"
+#endif
+
+#ifdef __x86_64__
+#include "pyconfig-x86_64.h"
+#endif
diff --git a/Makefile.pre.in b/Makefile.pre.in
index b5703fbe6ae974..d8df7de3102fbf 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -209,6 +209,12 @@ MACOSX_DEPLOYMENT_TARGET=@CONFIGURE_MACOSX_DEPLOYMENT_TARGET@
# the build, and is only listed here so it will be included in sysconfigdata.
IPHONEOS_DEPLOYMENT_TARGET=@IPHONEOS_DEPLOYMENT_TARGET@
+# visionOS Deployment target is *actually* used during the build, by the
+# compiler shims; export.
+XROS_DEPLOYMENT_TARGET=@XROS_DEPLOYMENT_TARGET@
+@EXPORT_XROS_DEPLOYMENT_TARGET@export XROS_DEPLOYMENT_TARGET
+
+
# Option to install to strip binaries
STRIPFLAG=-s
@@ -2264,31 +2270,64 @@ testuniversal: all
# a full Xcode install that has an iPhone SE (3rd edition) simulator available.
# This must be run *after* a `make install` has completed the build. The
# `--with-framework-name` argument *cannot* be used when configuring the build.
-XCFOLDER:=iOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID
+XCFOLDER-iOS:=iOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID
.PHONY: testios
testios:
@if test "$(MACHDEP)" != "ios"; then \
echo "Cannot run the iOS testbed for a non-iOS build."; \
exit 1;\
fi
+ @if test $(PYTHONFRAMEWORK) != "Python"; then \
+ echo "Cannot run the iOS testbed with a non-default framework name."; \
+ exit 1;\
+ fi
+ @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \
+ echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \
+ exit 1;\
+ fi
+
@if test "$(findstring -iphonesimulator,$(MULTIARCH))" != "-iphonesimulator"; then \
- echo "Cannot run the iOS testbed for non-simulator builds."; \
+ if test "$(findstring -macabi,$(MULTIARCH))" != "-macabi"; then \
+ echo "Cannot run the iOS testbed for device builds."; \
+ exit 1;\
+ else \
+ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed --catalyst clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)"; \
+ $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" --catalyst run --verbose -- test -uall --single-process --rerun -W; \
+ fi \
+ else \
+ $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)"; \
+ $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" run --verbose -- test -uall --single-process --rerun -W; \
+ fi
+
+# Run the test suite on the visionOS simulator. Must be run on a macOS machine with
+# a full Xcode install that has an Apple Vision Pro simulator available.
+# This must be run *after* a `make install` has completed the build. The
+# `--with-framework-name` argument *cannot* be used when configuring the build.
+XCFOLDER-visionOS:=visionOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID
+.PHONY: testvisionos
+testvisionos:
+ @if test "$(MACHDEP)" != "visionos"; then \
+ echo "Cannot run the visionOS testbed for a non-visionOS build."; \
+ exit 1;\
+ fi
+ @if test "$(findstring -xrsimulator,$(MULTIARCH))" != "-xrsimulator"; then \
+ echo "Cannot run the visionOS testbed for non-simulator builds."; \
exit 1;\
fi
@if test $(PYTHONFRAMEWORK) != "Python"; then \
- echo "Cannot run the iOS testbed with a non-default framework name."; \
+ echo "Cannot run the visionOS testbed with a non-default framework name."; \
exit 1;\
fi
@if ! test -d $(PYTHONFRAMEWORKPREFIX); then \
- echo "Cannot find a finalized iOS Python.framework. Have you run 'make install' to finalize the framework build?"; \
+ echo "Cannot find a finalized visionOS Python.framework. Have you run 'make install' to finalize the framework build?"; \
exit 1;\
fi
- # Clone the testbed project into the XCFOLDER
- $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)"
+ # Clone the testbed project into the XCFOLDER-visionOS
+ $(PYTHON_FOR_BUILD) $(srcdir)/visionOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-visionOS)"
# Run the testbed project
- $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W
+ $(PYTHON_FOR_BUILD) "$(XCFOLDER-visionOS)" run --verbose -- test -uall --single-process --rerun -W
# Like test, but using --slow-ci which enables all test resources and use
# longer timeout. Run an optional pybuildbot.identify script to include
@@ -2977,7 +3016,7 @@ frameworkinstallversionedstructure: $(LDLIBRARY)
fi; \
done
$(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers
- sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist
+ sed 's/%VERSION%/'"`$(RUNSHARED) ./$(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist
$(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current
$(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK)
$(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers
@@ -3005,6 +3044,14 @@ frameworkinstallunversionedstructure: $(LDLIBRARY)
$(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \
done
+# Stub compilation assistance binaries are installed separately on Mac Catalyst.
+.PHONY: frameworkinstallcatalyststubs
+frameworkinstallcatalyststubs: $(LDLIBRARY)
+ $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(BINDIR)
+ for file in $(srcdir)/$(RESSRCDIR)/bin/* ; do \
+ $(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \
+ done
+
# This installs Mac/Lib into the framework
# Install a number of symlinks to keep software that expects a normal unix
# install (which includes python-config) happy.
diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c
index f5cd73bdea8333..ba634ae16990d4 100644
--- a/Misc/platform_triplet.c
+++ b/Misc/platform_triplet.c
@@ -254,9 +254,41 @@ PLATFORM_TRIPLET=x86_64-iphonesimulator
# else
PLATFORM_TRIPLET=arm64-iphonesimulator
# endif
+# elif defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
+# if __x86_64__
+PLATFORM_TRIPLET=x86_64-iphoneos-macabi
+# else
+PLATFORM_TRIPLET=arm64-iphoneos-macabi
+# endif
# else
PLATFORM_TRIPLET=arm64-iphoneos
# endif
+# elif defined(TARGET_OS_TV) && TARGET_OS_TV
+# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+# if __x86_64__
+PLATFORM_TRIPLET=x86_64-appletvsimulator
+# else
+PLATFORM_TRIPLET=arm64-appletvsimulator
+# endif
+# else
+PLATFORM_TRIPLET=arm64-appletvos
+# endif
+# elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH
+# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+# if __x86_64__
+PLATFORM_TRIPLET=x86_64-watchsimulator
+# else
+PLATFORM_TRIPLET=arm64-watchsimulator
+# endif
+# else
+PLATFORM_TRIPLET=arm64_32-watchos
+# endif
+# elif defined(TARGET_OS_VISION) && TARGET_OS_VISION
+# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
+PLATFORM_TRIPLET=arm64-xrsimulator
+# else
+PLATFORM_TRIPLET=arm64-xros
+# endif
// Older macOS SDKs do not define TARGET_OS_OSX
# elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX
PLATFORM_TRIPLET=darwin
diff --git a/config.sub b/config.sub
index 1bb6a05dc11026..6efa3fbf821859 100755
--- a/config.sub
+++ b/config.sub
@@ -1743,7 +1743,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* | tvos* | watchos* \
+ | os9* | macos* | osx* | ios* | tvos* | watchos* | xros* \
| mpw* | magic* | mmixware* | mon960* | lnews* \
| amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
| aos* | aros* | cloudabi* | sortix* | twizzler* \
@@ -1769,7 +1769,7 @@ case $os in
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
- | fiwix* | mlibc* | cos* | mbr* | ironclad* )
+ | fiwix* | mlibc* | cos* | mbr* | ironclad* | macabi)
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
@@ -1867,7 +1867,9 @@ case $kernel-$os-$obj in
;;
*-eabi*- | *-gnueabi*-)
;;
- ios*-simulator- | tvos*-simulator- | watchos*-simulator- )
+ ios*-simulator- | tvos*-simulator- | watchos*-simulator- | xros*-simulator-)
+ ;;
+ ios*-macabi- )
;;
none--*)
# None (no kernel, i.e. freestanding / bare metal),
diff --git a/configure b/configure
index 884f8a4b0680d9..5710b745618708 100755
--- a/configure
+++ b/configure
@@ -982,6 +982,11 @@ LDFLAGS
CFLAGS
CC
HAS_XCRUN
+CATALYST_MACOS_VERSION
+EXPORT_XROS_DEPLOYMENT_TARGET
+XROS_DEPLOYMENT_TARGET
+WATCHOS_DEPLOYMENT_TARGET
+TVOS_DEPLOYMENT_TARGET
IPHONEOS_DEPLOYMENT_TARGET
EXPORT_MACOSX_DEPLOYMENT_TARGET
CONFIGURE_MACOSX_DEPLOYMENT_TARGET
@@ -1085,6 +1090,7 @@ with_universal_archs
with_framework_name
enable_framework
with_app_store_compliance
+with_catalyst_macos_version
enable_wasm_dynamic_linking
enable_wasm_pthreads
with_suffix
@@ -1874,6 +1880,9 @@ Optional Packages:
Enable any patches required for compiliance with app
stores. Optional PATCH-FILE specifies the custom
patch to apply.
+ --with-catalyst-macos-version=VER
+ The minimum macOS version a Catalyst build can run
+ on (only valid for *-apple-ios*-macabi targets)
--with-suffix=SUFFIX set executable suffix to SUFFIX (default is empty,
yes is mapped to '.exe')
--without-static-libpython
@@ -4116,6 +4125,15 @@ then
*-apple-ios*)
ac_sys_system=iOS
;;
+ *-apple-tvos*)
+ ac_sys_system=tvOS
+ ;;
+ *-apple-watchos*)
+ ac_sys_system=watchOS
+ ;;
+ *-apple-xros*)
+ ac_sys_system=visionOS
+ ;;
*-*-darwin*)
ac_sys_system=Darwin
;;
@@ -4197,7 +4215,7 @@ fi
# On cross-compile builds, configure will look for a host-specific compiler by
# prepending the user-provided host triple to the required binary name.
#
-# On iOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc",
+# On iOS/tvOS/watchOS/visionOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc",
# which isn't a binary that exists, and isn't very convenient, as it contains the
# iOS version. As the default cross-compiler name won't exist, configure falls
# back to gcc, which *definitely* won't work. We're providing wrapper scripts for
@@ -4209,33 +4227,89 @@ fi
# configure will fail.
if test -z "$AR"; then
case "$host" in
+ x86_64-apple-ios*-macabi) AR=x86_64-apple-ios-macabi-ar ;;
+ aarch64-apple-ios*-macabi) AR=arm64-apple-ios-macabi-ar ;;
+
+ x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;;
aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;;
aarch64-apple-ios*) AR=arm64-apple-ios-ar ;;
- x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;;
+
+ aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;;
+ aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;;
+ x86_64-apple-tvos*-simulator) AR=x86_64-apple-tvos-simulator-ar ;;
+
+ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;;
+ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;;
+ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;;
+
+ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;;
+ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;;
*)
esac
fi
if test -z "$CC"; then
case "$host" in
+ x86_64-apple-ios*-macabi) CC=x86_64-apple-ios-macabi-clang ;;
+ aarch64-apple-ios*-macabi) CC=arm64-apple-ios-macabi-clang ;;
+
+ x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;;
aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;;
aarch64-apple-ios*) CC=arm64-apple-ios-clang ;;
- x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;;
+
+ aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;;
+ aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;;
+ x86_64-apple-tvos*-simulator) CC=x86_64-apple-tvos-simulator-clang ;;
+
+ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;;
+ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;;
+ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;;
+
+ aarch64-apple-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;;
+ aarch64-apple-xros*) CC=arm64-apple-xros-clang ;;
*)
esac
fi
if test -z "$CPP"; then
case "$host" in
+ x86_64-apple-ios*-macabi) CPP=x86_64-apple-ios-macabi-cpp ;;
+ aarch64-apple-ios*-macabi) CPP=arm64-apple-ios-macabi-cpp ;;
+
+ x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;;
aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;;
aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;;
- x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;;
+
+ aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;;
+ aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;;
+ x86_64-apple-tvos*-simulator) CPP=x86_64-apple-tvos-simulator-cpp ;;
+
+ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;;
+ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;;
+ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;;
+
+ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;;
+ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;;
*)
esac
fi
if test -z "$CXX"; then
case "$host" in
+ x86_64-apple-ios*-macabi) CXX=x86_64-apple-ios-macabi-clang++ ;;
+ aarch64-apple-ios*-macabi) CXX=arm64-apple-ios-macabi-clang++ ;;
+
+ x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;;
aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;;
aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;;
- x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;;
+
+ aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;;
+ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;;
+ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang++ ;;
+
+ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang++ ;;
+ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang++ ;;
+ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang++ ;;
+
+ aarch64-apple-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;;
+ aarch64-apple-xros*) CXX=arm64-apple-xros-clang++ ;;
*)
esac
fi
@@ -4358,8 +4432,11 @@ then :
case $enableval in
yes)
case $ac_sys_system in
- Darwin) enableval=/Library/Frameworks ;;
- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;;
+ Darwin) enableval=/Library/Frameworks ;;
+ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;;
+ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;;
+ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;;
+ visionOS) enableval=visionOS/Frameworks/\$\(MULTIARCH\) ;;
*) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5
esac
esac
@@ -4368,6 +4445,9 @@ then :
no)
case $ac_sys_system in
iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;;
+ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;;
+ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;;
+ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;;
*)
PYTHONFRAMEWORK=
PYTHONFRAMEWORKDIR=no-framework
@@ -4461,6 +4541,36 @@ then :
;;
iOS) :
+ case $(echo $host | cut -d '-' -f4) in
+ macabi)
+ FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs "
+ FRAMEWORKINSTALLLAST=""
+ FRAMEWORKALTINSTALLLAST=""
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR/Versions/$VERSION"
+ RESSRCDIR=MacCatalyst/Resources
+ ac_config_files="$ac_config_files MacCatalyst/Resources/Info.plist"
+
+ ;;
+ *)
+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+ prefix=$PYTHONFRAMEWORKPREFIX
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
+ RESSRCDIR=iOS/Resources
+ ac_config_files="$ac_config_files iOS/Resources/Info.plist"
+
+ ;;
+ esac
+ ;;
+ tvOS) :
FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
@@ -4470,9 +4580,39 @@ then :
prefix=$PYTHONFRAMEWORKPREFIX
PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
- RESSRCDIR=iOS/Resources
+ RESSRCDIR=tvOS/Resources
- ac_config_files="$ac_config_files iOS/Resources/Info.plist"
+ ac_config_files="$ac_config_files tvOS/Resources/Info.plist"
+
+ ;;
+ watchOS) :
+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+
+ prefix=$PYTHONFRAMEWORKPREFIX
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
+ RESSRCDIR=watchOS/Resources
+
+ ac_config_files="$ac_config_files watchOS/Resources/Info.plist"
+
+ ;;
+ visionOS) :
+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+
+ prefix=$PYTHONFRAMEWORKPREFIX
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
+ RESSRCDIR=visionOS/Resources
+
+ ac_config_files="$ac_config_files visionOS/Resources/Info.plist"
;;
*)
@@ -4485,6 +4625,9 @@ else case e in #(
e)
case $ac_sys_system in
iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;;
+ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;;
+ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;;
+ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;;
*)
PYTHONFRAMEWORK=
PYTHONFRAMEWORKDIR=no-framework
@@ -4539,8 +4682,8 @@ then :
case "$withval" in
yes)
case $ac_sys_system in
- Darwin|iOS)
- # iOS is able to share the macOS patch
+ Darwin|iOS|tvOS|watchOS|visionOS)
+ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch
APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch"
;;
*) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;;
@@ -4558,8 +4701,8 @@ printf "%s\n" "applying custom app store compliance patch" >&6; }
else case e in #(
e)
case $ac_sys_system in
- iOS)
- # Always apply the compliance patch on iOS; we can use the macOS patch
+ iOS|tvOS|watchOS|visionOS)
+ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch
APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5
printf "%s\n" "applying default app store compliance patch" >&6; }
@@ -4578,6 +4721,36 @@ fi
+
+# Check whether --with-catalyst-macos-version was given.
+if test ${with_catalyst_macos_version+y}
+then :
+ withval=$with_catalyst_macos_version; case "$host" in
+ *-apple-ios*-macabi)
+ CATALYST_MACOS_VERSION="$withval"
+ ;;
+ *)
+ as_fn_error $? "--with-catalyst-macos-version is only valid when targeting Mac Catalyst (*-apple-ios*-macabi)." "$LINENO" 5
+ ;;
+ esac
+
+else case e in #(
+ e) case "$host" in
+ *-apple-ios*-macabi)
+ CATALYST_MACOS_VERSION=11.2
+ ;;
+ *)
+ CATALYST_MACOS_VERSION=
+ ;;
+ esac
+
+ ;;
+esac
+fi
+
+EXPORT_XROS_DEPLOYMENT_TARGET='#'
+
+
if test "$cross_compiling" = yes; then
case "$host" in
*-*-linux*)
@@ -4592,6 +4765,28 @@ if test "$cross_compiling" = yes; then
*-*-cygwin*)
_host_ident=
;;
+ *-apple-ios*-macabi)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4` # should be macabi
+ _host_device=${_host_device:=os}
+
+ # IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking iOS deployment target" >&5
+printf %s "checking iOS deployment target... " >&6; }
+ IPHONEOS_DEPLOYMENT_TARGET=$(echo ${_host_os} | cut -c4-)
+ IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=14.2} # else it returns invalid version number
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPHONEOS_DEPLOYMENT_TARGET" >&5
+printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; }
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphoneos-${_host_device} # platform_triplet.c uses iphoneos-macabi
+ ;;
+ *)
+ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphoneos-${_host_device}
+ ;;
+ esac
+ ;;
*-apple-ios*)
_host_os=`echo $host | cut -d '-' -f3`
_host_device=`echo $host | cut -d '-' -f4`
@@ -4614,6 +4809,78 @@ printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; }
;;
esac
;;
+ *-apple-tvos*)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4`
+ _host_device=${_host_device:=os}
+
+ # TVOS_DEPLOYMENT_TARGET is the minimum supported tvOS version
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking tvOS deployment target" >&5
+printf %s "checking tvOS deployment target... " >&6; }
+ TVOS_DEPLOYMENT_TARGET=${_host_os:4}
+ TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:=12.0}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TVOS_DEPLOYMENT_TARGET" >&5
+printf "%s\n" "$TVOS_DEPLOYMENT_TARGET" >&6; }
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-arm64-appletv${_host_device}
+ ;;
+ *)
+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-$host_cpu-appletv${_host_device}
+ ;;
+ esac
+ ;;
+ *-apple-watchos*)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4`
+ _host_device=${_host_device:=os}
+
+ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking watchOS deployment target" >&5
+printf %s "checking watchOS deployment target... " >&6; }
+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7}
+ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $WATCHOS_DEPLOYMENT_TARGET" >&5
+printf "%s\n" "$WATCHOS_DEPLOYMENT_TARGET" >&6; }
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device}
+ ;;
+ *)
+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device}
+ ;;
+ esac
+ ;;
+ *-apple-xros*)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4`
+ _host_device=${_host_device:=os}
+
+ # XROS_DEPLOYMENT_TARGET is the minimum supported visionOS version
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking visionOS deployment target" >&5
+printf %s "checking visionOS deployment target... " >&6; }
+ XROS_DEPLOYMENT_TARGET=${_host_os:8}
+ XROS_DEPLOYMENT_TARGET=${XROS_DEPLOYMENT_TARGET:=2.0}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $XROS_DEPLOYMENT_TARGET" >&5
+printf "%s\n" "$XROS_DEPLOYMENT_TARGET" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking exporting flag of visionOS deployment target" >&5
+printf %s "checking exporting flag of visionOS deployment target... " >&6; }
+ export XROS_DEPLOYMENT_TARGET
+ EXPORT_XROS_DEPLOYMENT_TARGET=''
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $EXPORT_XROS_DEPLOYMENT_TARGET" >&5
+printf "%s\n" "$EXPORT_XROS_DEPLOYMENT_TARGET" >&6; }
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device}
+ ;;
+ *)
+ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device}
+ ;;
+ esac
+ ;;
*-*-darwin*)
case "$host_cpu" in
arm*)
@@ -4704,9 +4971,15 @@ 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.
+ # On iOS/tvOS/watchOS/visionOS, defining _POSIX_C_SOURCE also disables platform specific features.
iOS/*)
define_xopen_source=no;;
+ tvOS/*)
+ define_xopen_source=no;;
+ watchOS/*)
+ define_xopen_source=no;;
+ visionOS/*)
+ define_xopen_source=no;;
# On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from
# defining NI_NUMERICHOST.
QNX/6.3.2)
@@ -4769,7 +5042,17 @@ fi
CONFIGURE_MACOSX_DEPLOYMENT_TARGET=
EXPORT_MACOSX_DEPLOYMENT_TARGET='#'
-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple.
+# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET /
+# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple.
+
+
+
+
+# XROS_DEPLOYMENT_TARGET should get exported
+
+
+# The minimum macOS version that a Mac Catalyst build can run on.
+
# checks for alternative programs
@@ -4810,6 +5093,16 @@ case $ac_sys_system in #(
as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"
as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"
;; #(
+ tvOS) :
+
+ as_fn_append CFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"
+ as_fn_append LDFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"
+ ;; #(
+ watchOS) :
+
+ as_fn_append CFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"
+ as_fn_append LDFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"
+ ;; #(
*) :
;;
esac
@@ -7179,6 +7472,12 @@ case $ac_sys_system in #(
MULTIARCH="" ;; #(
iOS) :
MULTIARCH="" ;; #(
+ tvOS) :
+ MULTIARCH="" ;; #(
+ watchOS) :
+ MULTIARCH="" ;; #(
+ visionOS) :
+ MULTIARCH="" ;; #(
FreeBSD*) :
MULTIARCH="" ;; #(
*) :
@@ -7199,7 +7498,7 @@ fi
printf "%s\n" "$MULTIARCH" >&6; }
case $ac_sys_system in #(
- iOS) :
+ iOS|tvOS|watchOS|visionOS) :
SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #(
*) :
SOABI_PLATFORM=$PLATFORM_TRIPLET
@@ -7246,10 +7545,26 @@ case $host/$ac_cv_cc_name in #(
PY_SUPPORT_TIER=3 ;; #(
x86_64-*-freebsd*/clang) :
PY_SUPPORT_TIER=3 ;; #(
+ aarch64-apple-ios*-macabi/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
+ x86_64-apple-ios*-macabi/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
aarch64-apple-ios*-simulator/clang) :
PY_SUPPORT_TIER=3 ;; #(
aarch64-apple-ios*/clang) :
PY_SUPPORT_TIER=3 ;; #(
+ aarch64-apple-tvos*-simulator/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
+ aarch64-apple-tvos*/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
+ aarch64-apple-watchos*-simulator/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
+ arm64_32-apple-watchos*/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
+ aarch64-apple-xros*-simulator/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
+ aarch64-apple-xros*/clang) :
+ PY_SUPPORT_TIER=3 ;; #(
aarch64-*-linux-android/clang) :
PY_SUPPORT_TIER=3 ;; #(
x86_64-*-linux-android/clang) :
@@ -7686,7 +8001,7 @@ then
case $ac_sys_system in
Darwin)
LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';;
- iOS)
+ iOS|tvOS|watchOS|visionOS)
LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';;
*)
as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;;
@@ -7752,7 +8067,7 @@ printf "%s\n" "#define Py_ENABLE_SHARED 1" >>confdefs.h
BLDLIBRARY='-L. -lpython$(LDVERSION)'
RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}}
;;
- iOS)
+ iOS|tvOS|watchOS|visionOS)
LDLIBRARY='libpython$(LDVERSION).dylib'
;;
AIX*)
@@ -13574,7 +13889,7 @@ then
BLDSHARED="$LDSHARED"
fi
;;
- iOS/*)
+ iOS/*|tvOS/*|watchOS/*|visionOS/*)
LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)'
LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)'
BLDSHARED="$LDSHARED"
@@ -13707,7 +14022,7 @@ then
Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";;
Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";;
# -u libsys_s pulls in all symbols in libsys
- Darwin/*|iOS/*)
+ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*)
LINKFORSHARED="$extra_undefs -framework CoreFoundation"
# Issue #18075: the default maximum stack size (8MBytes) is too
@@ -13731,7 +14046,7 @@ printf "%s\n" "#define THREAD_STACK_SIZE 0x$stack_size" >>confdefs.h
LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)'
fi
LINKFORSHARED="$LINKFORSHARED"
- elif test $ac_sys_system = "iOS"; then
+ elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS"; then
LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)'
fi
;;
@@ -15508,7 +15823,7 @@ then :
ctypes_malloc_closure=yes
;; #(
- iOS) :
+ iOS|tvOS|watchOS|visionOS) :
ctypes_malloc_closure=yes
;; #(
@@ -19260,12 +19575,6 @@ if test "x$ac_cv_func_dup3" = xyes
then :
printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h
-fi
-ac_fn_c_check_func "$LINENO" "execv" "ac_cv_func_execv"
-if test "x$ac_cv_func_execv" = xyes
-then :
- printf "%s\n" "#define HAVE_EXECV 1" >>confdefs.h
-
fi
ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
if test "x$ac_cv_func_explicit_bzero" = xyes
@@ -19326,18 +19635,6 @@ if test "x$ac_cv_func_fexecve" = xyes
then :
printf "%s\n" "#define HAVE_FEXECVE 1" >>confdefs.h
-fi
-ac_fn_c_check_func "$LINENO" "fork" "ac_cv_func_fork"
-if test "x$ac_cv_func_fork" = xyes
-then :
- printf "%s\n" "#define HAVE_FORK 1" >>confdefs.h
-
-fi
-ac_fn_c_check_func "$LINENO" "fork1" "ac_cv_func_fork1"
-if test "x$ac_cv_func_fork1" = xyes
-then :
- printf "%s\n" "#define HAVE_FORK1 1" >>confdefs.h
-
fi
ac_fn_c_check_func "$LINENO" "fpathconf" "ac_cv_func_fpathconf"
if test "x$ac_cv_func_fpathconf" = xyes
@@ -19764,24 +20061,6 @@ if test "x$ac_cv_func_posix_openpt" = xyes
then :
printf "%s\n" "#define HAVE_POSIX_OPENPT 1" >>confdefs.h
-fi
-ac_fn_c_check_func "$LINENO" "posix_spawn" "ac_cv_func_posix_spawn"
-if test "x$ac_cv_func_posix_spawn" = xyes
-then :
- printf "%s\n" "#define HAVE_POSIX_SPAWN 1" >>confdefs.h
-
-fi
-ac_fn_c_check_func "$LINENO" "posix_spawnp" "ac_cv_func_posix_spawnp"
-if test "x$ac_cv_func_posix_spawnp" = xyes
-then :
- printf "%s\n" "#define HAVE_POSIX_SPAWNP 1" >>confdefs.h
-
-fi
-ac_fn_c_check_func "$LINENO" "posix_spawn_file_actions_addclosefrom_np" "ac_cv_func_posix_spawn_file_actions_addclosefrom_np"
-if test "x$ac_cv_func_posix_spawn_file_actions_addclosefrom_np" = xyes
-then :
- printf "%s\n" "#define HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP 1" >>confdefs.h
-
fi
ac_fn_c_check_func "$LINENO" "pread" "ac_cv_func_pread"
if test "x$ac_cv_func_pread" = xyes
@@ -20100,12 +20379,6 @@ if test "x$ac_cv_func_sigaction" = xyes
then :
printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h
-fi
-ac_fn_c_check_func "$LINENO" "sigaltstack" "ac_cv_func_sigaltstack"
-if test "x$ac_cv_func_sigaltstack" = xyes
-then :
- printf "%s\n" "#define HAVE_SIGALTSTACK 1" >>confdefs.h
-
fi
ac_fn_c_check_func "$LINENO" "sigfillset" "ac_cv_func_sigfillset"
if test "x$ac_cv_func_sigfillset" = xyes
@@ -20374,11 +20647,11 @@ fi
fi
-# iOS defines some system methods that can be linked (so they are
+# iOS/tvOS/watchOS/visionOS define some system methods that can be linked (so they are
# found by configure), but either raise a compilation error (because the
# header definition prevents usage - autoconf doesn't use the headers), or
# raise an error if used at runtime. Force these symbols off.
-if test "$ac_sys_system" != "iOS" ; then
+if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then
ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy"
if test "x$ac_cv_func_getentropy" = xyes
then :
@@ -20400,6 +20673,53 @@ fi
fi
+# tvOS/watchOS have some additional methods that can be found, but not used.
+if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then
+ ac_fn_c_check_func "$LINENO" "execv" "ac_cv_func_execv"
+if test "x$ac_cv_func_execv" = xyes
+then :
+ printf "%s\n" "#define HAVE_EXECV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fork" "ac_cv_func_fork"
+if test "x$ac_cv_func_fork" = xyes
+then :
+ printf "%s\n" "#define HAVE_FORK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fork1" "ac_cv_func_fork1"
+if test "x$ac_cv_func_fork1" = xyes
+then :
+ printf "%s\n" "#define HAVE_FORK1 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "posix_spawn" "ac_cv_func_posix_spawn"
+if test "x$ac_cv_func_posix_spawn" = xyes
+then :
+ printf "%s\n" "#define HAVE_POSIX_SPAWN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "posix_spawnp" "ac_cv_func_posix_spawnp"
+if test "x$ac_cv_func_posix_spawnp" = xyes
+then :
+ printf "%s\n" "#define HAVE_POSIX_SPAWNP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "posix_spawn_file_actions_addclosefrom_np" "ac_cv_func_posix_spawn_file_actions_addclosefrom_np"
+if test "x$ac_cv_func_posix_spawn_file_actions_addclosefrom_np" = xyes
+then :
+ printf "%s\n" "#define HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigaltstack" "ac_cv_func_sigaltstack"
+if test "x$ac_cv_func_sigaltstack" = xyes
+then :
+ printf "%s\n" "#define HAVE_SIGALTSTACK 1" >>confdefs.h
+
+fi
+
+fi
+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5
printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; }
if test ${ac_cv_c_undeclared_builtin_options+y}
@@ -23844,7 +24164,8 @@ fi
# check for openpty, login_tty, and forkpty
-
+# tvOS/watchOS have functions for tty, but can't use them
+if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then
for ac_func in openpty
do :
@@ -23958,7 +24279,7 @@ esac
fi
done
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing login_tty" >&5
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing login_tty" >&5
printf %s "checking for library containing login_tty... " >&6; }
if test ${ac_cv_search_login_tty+y}
then :
@@ -24141,6 +24462,7 @@ esac
fi
done
+fi
# check for long file support functions
ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64"
@@ -24406,10 +24728,10 @@ fi
done
-# On Android and iOS, clock_settime can be linked (so it is found by
+# On Android, iOS, tvOS, watchOS, and visionOS, clock_settime can be linked (so it is found by
# configure), but when used in an unprivileged process, it crashes rather than
# returning an error. Force the symbol off.
-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS"
+if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS"
then
for ac_func in clock_settime
@@ -24726,7 +25048,7 @@ else case e in #(
e) if test "$cross_compiling" = yes
then :
-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then
+if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" || test "$ac_sys_system" = "visionOS"; then
ac_cv_buggy_getaddrinfo="no"
elif test "${enable_ipv6+set}" = set; then
ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6"
@@ -26748,8 +27070,8 @@ if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MA
LIBPYTHON="\$(BLDLIBRARY)"
fi
-# On iOS the shared libraries must be linked with the Python framework
-if test "$ac_sys_system" = "iOS"; then
+# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework
+if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then
MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)"
fi
@@ -29619,7 +29941,7 @@ LIBS=$save_LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5
printf "%s\n" "$as_me: checking for device files" >&6;}
-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then
+if test "$ac_sys_system" = "Linux-android" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS" ; then
ac_cv_file__dev_ptmx=no
ac_cv_file__dev_ptc=no
else
@@ -30129,7 +30451,7 @@ else case e in #(
with_ensurepip=no ;; #(
WASI) :
with_ensurepip=no ;; #(
- iOS) :
+ iOS|tvOS|watchOS|visionOS) :
with_ensurepip=no ;; #(
*) :
with_ensurepip=upgrade
@@ -31078,7 +31400,7 @@ case "$ac_sys_system" in
SunOS*) _PYTHREAD_NAME_MAXLEN=31;;
NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268
Darwin) _PYTHREAD_NAME_MAXLEN=63;;
- iOS) _PYTHREAD_NAME_MAXLEN=63;;
+ iOS|tvOS|watchOS|visionOS) _PYTHREAD_NAME_MAXLEN=63;;
FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268
OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268
*) _PYTHREAD_NAME_MAXLEN=;;
@@ -31110,8 +31432,15 @@ case $ac_sys_system in #(
;; #(
Darwin) :
;; #(
- iOS) :
+ iOS|tvOS|watchOS|visionOS) :
+ case "$_host_device" in
+ macabi) ;;
+ *)
+
+ py_cv_module__posixsubprocess=n/a
+ ;;
+ esac
py_cv_module__curses=n/a
@@ -31119,7 +31448,6 @@ case $ac_sys_system in #(
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_grp=n/a
@@ -35271,7 +35599,11 @@ do
"Mac/PythonLauncher/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/PythonLauncher/Makefile" ;;
"Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;;
"Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;;
+ "MacCatalyst/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES MacCatalyst/Resources/Info.plist" ;;
"iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;;
+ "tvOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES tvOS/Resources/Info.plist" ;;
+ "watchOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES watchOS/Resources/Info.plist" ;;
+ "visionOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES visionOS/Resources/Info.plist" ;;
"Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;;
"Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;;
"Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;;
diff --git a/configure.ac b/configure.ac
index cf25148bad2077..1c47cbef67da84 100644
--- a/configure.ac
+++ b/configure.ac
@@ -330,6 +330,15 @@ then
*-apple-ios*)
ac_sys_system=iOS
;;
+ *-apple-tvos*)
+ ac_sys_system=tvOS
+ ;;
+ *-apple-watchos*)
+ ac_sys_system=watchOS
+ ;;
+ *-apple-xros*)
+ ac_sys_system=visionOS
+ ;;
*-*-darwin*)
ac_sys_system=Darwin
;;
@@ -405,7 +414,7 @@ AC_SUBST([host_exec_prefix])
# On cross-compile builds, configure will look for a host-specific compiler by
# prepending the user-provided host triple to the required binary name.
#
-# On iOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc",
+# On iOS/tvOS/watchOS/visionOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc",
# which isn't a binary that exists, and isn't very convenient, as it contains the
# iOS version. As the default cross-compiler name won't exist, configure falls
# back to gcc, which *definitely* won't work. We're providing wrapper scripts for
@@ -417,33 +426,89 @@ AC_SUBST([host_exec_prefix])
# configure will fail.
if test -z "$AR"; then
case "$host" in
+ x86_64-apple-ios*-macabi) AR=x86_64-apple-ios-macabi-ar ;;
+ aarch64-apple-ios*-macabi) AR=arm64-apple-ios-macabi-ar ;;
+
+ x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;;
aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;;
aarch64-apple-ios*) AR=arm64-apple-ios-ar ;;
- x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;;
+
+ aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;;
+ aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;;
+ x86_64-apple-tvos*-simulator) AR=x86_64-apple-tvos-simulator-ar ;;
+
+ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;;
+ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;;
+ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;;
+
+ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;;
+ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;;
*)
esac
fi
if test -z "$CC"; then
case "$host" in
+ x86_64-apple-ios*-macabi) CC=x86_64-apple-ios-macabi-clang ;;
+ aarch64-apple-ios*-macabi) CC=arm64-apple-ios-macabi-clang ;;
+
+ x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;;
aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;;
aarch64-apple-ios*) CC=arm64-apple-ios-clang ;;
- x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;;
+
+ aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;;
+ aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;;
+ x86_64-apple-tvos*-simulator) CC=x86_64-apple-tvos-simulator-clang ;;
+
+ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;;
+ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;;
+ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;;
+
+ aarch64-apple-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;;
+ aarch64-apple-xros*) CC=arm64-apple-xros-clang ;;
*)
esac
fi
if test -z "$CPP"; then
case "$host" in
+ x86_64-apple-ios*-macabi) CPP=x86_64-apple-ios-macabi-cpp ;;
+ aarch64-apple-ios*-macabi) CPP=arm64-apple-ios-macabi-cpp ;;
+
+ x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;;
aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;;
aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;;
- x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;;
+
+ aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;;
+ aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;;
+ x86_64-apple-tvos*-simulator) CPP=x86_64-apple-tvos-simulator-cpp ;;
+
+ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;;
+ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;;
+ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;;
+
+ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;;
+ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;;
*)
esac
fi
if test -z "$CXX"; then
case "$host" in
+ x86_64-apple-ios*-macabi) CXX=x86_64-apple-ios-macabi-clang++ ;;
+ aarch64-apple-ios*-macabi) CXX=arm64-apple-ios-macabi-clang++ ;;
+
+ x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;;
aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;;
aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;;
- x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;;
+
+ aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;;
+ aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;;
+ x86_64-apple-tvos*-simulator) CXX=x86_64-apple-tvos-simulator-clang++ ;;
+
+ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang++ ;;
+ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang++ ;;
+ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang++ ;;
+
+ aarch64-apple-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;;
+ aarch64-apple-xros*) CXX=arm64-apple-xros-clang++ ;;
*)
esac
fi
@@ -558,8 +623,11 @@ AC_ARG_ENABLE([framework],
case $enableval in
yes)
case $ac_sys_system in
- Darwin) enableval=/Library/Frameworks ;;
- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;;
+ Darwin) enableval=/Library/Frameworks ;;
+ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;;
+ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;;
+ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;;
+ visionOS) enableval=visionOS/Frameworks/\$\(MULTIARCH\) ;;
*) AC_MSG_ERROR([Unknown platform for framework build])
esac
esac
@@ -568,6 +636,9 @@ AC_ARG_ENABLE([framework],
no)
case $ac_sys_system in
iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;;
+ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;;
+ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;;
+ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;;
*)
PYTHONFRAMEWORK=
PYTHONFRAMEWORKDIR=no-framework
@@ -657,6 +728,34 @@ AC_ARG_ENABLE([framework],
AC_CONFIG_FILES([Mac/Resources/app/Info.plist])
;;
iOS) :
+ case $(echo $host | cut -d '-' -f4) in
+ macabi)
+ FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs "
+ FRAMEWORKINSTALLLAST=""
+ FRAMEWORKALTINSTALLLAST=""
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+ prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR/Versions/$VERSION"
+ RESSRCDIR=MacCatalyst/Resources
+ AC_CONFIG_FILES([MacCatalyst/Resources/Info.plist])
+ ;;
+ *)
+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+ prefix=$PYTHONFRAMEWORKPREFIX
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
+ RESSRCDIR=iOS/Resources
+ AC_CONFIG_FILES([iOS/Resources/Info.plist])
+ ;;
+ esac
+ ;;
+ tvOS) :
FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
@@ -666,9 +765,37 @@ AC_ARG_ENABLE([framework],
prefix=$PYTHONFRAMEWORKPREFIX
PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
- RESSRCDIR=iOS/Resources
+ RESSRCDIR=tvOS/Resources
- AC_CONFIG_FILES([iOS/Resources/Info.plist])
+ AC_CONFIG_FILES([tvOS/Resources/Info.plist])
+ ;;
+ watchOS) :
+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+
+ prefix=$PYTHONFRAMEWORKPREFIX
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
+ RESSRCDIR=watchOS/Resources
+
+ AC_CONFIG_FILES([watchOS/Resources/Info.plist])
+ ;;
+ visionOS) :
+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure"
+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure "
+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders"
+ FRAMEWORKPYTHONW=
+ INSTALLTARGETS="libinstall inclinstall sharedinstall"
+
+ prefix=$PYTHONFRAMEWORKPREFIX
+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR"
+ RESSRCDIR=visionOS/Resources
+
+ AC_CONFIG_FILES([visionOS/Resources/Info.plist])
;;
*)
AC_MSG_ERROR([Unknown platform for framework build])
@@ -678,6 +805,9 @@ AC_ARG_ENABLE([framework],
],[
case $ac_sys_system in
iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;;
+ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;;
+ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;;
+ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;;
*)
PYTHONFRAMEWORK=
PYTHONFRAMEWORKDIR=no-framework
@@ -730,8 +860,8 @@ AC_ARG_WITH(
case "$withval" in
yes)
case $ac_sys_system in
- Darwin|iOS)
- # iOS is able to share the macOS patch
+ Darwin|iOS|tvOS|watchOS|visionOS)
+ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch
APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch"
;;
*) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;;
@@ -745,8 +875,8 @@ AC_ARG_WITH(
esac
],[
case $ac_sys_system in
- iOS)
- # Always apply the compliance patch on iOS; we can use the macOS patch
+ iOS|tvOS|watchOS|visionOS)
+ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch
APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch"
AC_MSG_RESULT([applying default app store compliance patch])
;;
@@ -759,6 +889,31 @@ AC_ARG_WITH(
])
AC_SUBST([APP_STORE_COMPLIANCE_PATCH])
+
+AC_ARG_WITH([catalyst-macos-version],
+ [AS_HELP_STRING([--with-catalyst-macos-version=VER],
+ [The minimum macOS version a Catalyst build can run on (only valid for *-apple-ios*-macabi targets)])],
+ [case "$host" in
+ *-apple-ios*-macabi)
+ CATALYST_MACOS_VERSION="$withval"
+ ;;
+ *)
+ AC_MSG_ERROR([--with-catalyst-macos-version is only valid when targeting Mac Catalyst (*-apple-ios*-macabi).])
+ ;;
+ esac
+ ],
+ [case "$host" in
+ *-apple-ios*-macabi)
+ CATALYST_MACOS_VERSION=11.2
+ ;;
+ *)
+ CATALYST_MACOS_VERSION=
+ ;;
+ esac
+ ]
+)
+EXPORT_XROS_DEPLOYMENT_TARGET='#'
+
AC_SUBST([_PYTHON_HOST_PLATFORM])
if test "$cross_compiling" = yes; then
case "$host" in
@@ -774,6 +929,26 @@ if test "$cross_compiling" = yes; then
*-*-cygwin*)
_host_ident=
;;
+ *-apple-ios*-macabi)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4` # should be macabi
+ _host_device=${_host_device:=os}
+
+ # IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version
+ AC_MSG_CHECKING([iOS deployment target])
+ IPHONEOS_DEPLOYMENT_TARGET=$(echo ${_host_os} | cut -c4-)
+ IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=14.2} # else it returns invalid version number
+ AC_MSG_RESULT([$IPHONEOS_DEPLOYMENT_TARGET])
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphoneos-${_host_device} # platform_triplet.c uses iphoneos-macabi
+ ;;
+ *)
+ _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphoneos-${_host_device}
+ ;;
+ esac
+ ;;
*-apple-ios*)
_host_os=`echo $host | cut -d '-' -f3`
_host_device=`echo $host | cut -d '-' -f4`
@@ -794,6 +969,70 @@ if test "$cross_compiling" = yes; then
;;
esac
;;
+ *-apple-tvos*)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4`
+ _host_device=${_host_device:=os}
+
+ # TVOS_DEPLOYMENT_TARGET is the minimum supported tvOS version
+ AC_MSG_CHECKING([tvOS deployment target])
+ TVOS_DEPLOYMENT_TARGET=${_host_os:4}
+ TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:=12.0}
+ AC_MSG_RESULT([$TVOS_DEPLOYMENT_TARGET])
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-arm64-appletv${_host_device}
+ ;;
+ *)
+ _host_ident=${TVOS_DEPLOYMENT_TARGET}-$host_cpu-appletv${_host_device}
+ ;;
+ esac
+ ;;
+ *-apple-watchos*)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4`
+ _host_device=${_host_device:=os}
+
+ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version
+ AC_MSG_CHECKING([watchOS deployment target])
+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7}
+ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0}
+ AC_MSG_RESULT([$WATCHOS_DEPLOYMENT_TARGET])
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device}
+ ;;
+ *)
+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device}
+ ;;
+ esac
+ ;;
+ *-apple-xros*)
+ _host_os=`echo $host | cut -d '-' -f3`
+ _host_device=`echo $host | cut -d '-' -f4`
+ _host_device=${_host_device:=os}
+
+ # XROS_DEPLOYMENT_TARGET is the minimum supported visionOS version
+ AC_MSG_CHECKING([visionOS deployment target])
+ XROS_DEPLOYMENT_TARGET=${_host_os:8}
+ XROS_DEPLOYMENT_TARGET=${XROS_DEPLOYMENT_TARGET:=2.0}
+ AC_MSG_RESULT([$XROS_DEPLOYMENT_TARGET])
+ AC_MSG_CHECKING([exporting flag of visionOS deployment target])
+ export XROS_DEPLOYMENT_TARGET
+ EXPORT_XROS_DEPLOYMENT_TARGET=''
+ AC_MSG_RESULT([$EXPORT_XROS_DEPLOYMENT_TARGET])
+
+ case "$host_cpu" in
+ aarch64)
+ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device}
+ ;;
+ *)
+ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device}
+ ;;
+ esac
+ ;;
*-*-darwin*)
case "$host_cpu" in
arm*)
@@ -883,9 +1122,15 @@ 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.
+ # On iOS/tvOS/watchOS/visionOS, defining _POSIX_C_SOURCE also disables platform specific features.
iOS/*)
define_xopen_source=no;;
+ tvOS/*)
+ define_xopen_source=no;;
+ watchOS/*)
+ define_xopen_source=no;;
+ visionOS/*)
+ define_xopen_source=no;;
# On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from
# defining NI_NUMERICHOST.
QNX/6.3.2)
@@ -944,8 +1189,18 @@ AC_SUBST([EXPORT_MACOSX_DEPLOYMENT_TARGET])
CONFIGURE_MACOSX_DEPLOYMENT_TARGET=
EXPORT_MACOSX_DEPLOYMENT_TARGET='#'
-# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple.
+# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET /
+# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple.
AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET])
+AC_SUBST([TVOS_DEPLOYMENT_TARGET])
+AC_SUBST([WATCHOS_DEPLOYMENT_TARGET])
+AC_SUBST([XROS_DEPLOYMENT_TARGET])
+# XROS_DEPLOYMENT_TARGET should get exported
+AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET])
+
+# The minimum macOS version that a Mac Catalyst build can run on.
+AC_SUBST([CATALYST_MACOS_VERSION])
+
# checks for alternative programs
@@ -979,11 +1234,19 @@ AS_CASE([$host],
],
)
-dnl Add the compiler flag for the iOS minimum supported OS version.
+dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS
+dnl version. visionOS doesn't use an explicit -mxros-version-min option -
+dnl it encodes the min version into the target triple.
AS_CASE([$ac_sys_system],
[iOS], [
AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"])
+ ],[tvOS], [
+ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"])
+ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"])
+ ],[watchOS], [
+ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"])
+ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"])
],
)
@@ -1172,6 +1435,9 @@ AC_MSG_CHECKING([for multiarch])
AS_CASE([$ac_sys_system],
[Darwin*], [MULTIARCH=""],
[iOS], [MULTIARCH=""],
+ [tvOS], [MULTIARCH=""],
+ [watchOS], [MULTIARCH=""],
+ [visionOS], [MULTIARCH=""],
[FreeBSD*], [MULTIARCH=""],
[MULTIARCH=$($CC --print-multiarch 2>/dev/null)]
)
@@ -1193,7 +1459,7 @@ dnl will have multiple sysconfig modules (one for each CPU architecture), but
dnl use a single "fat" binary at runtime. SOABI_PLATFORM is the component of
dnl the PLATFORM_TRIPLET that will be used in binary module extensions.
AS_CASE([$ac_sys_system],
- [iOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`],
+ [iOS|tvOS|watchOS|visionOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`],
[SOABI_PLATFORM=$PLATFORM_TRIPLET]
)
@@ -1225,8 +1491,16 @@ AS_CASE([$host/$ac_cv_cc_name],
[powerpc64le-*-linux-gnu/clang], [PY_SUPPORT_TIER=3], dnl Linux on PPC64 little endian, glibc, clang
[s390x-*-linux-gnu/gcc], [PY_SUPPORT_TIER=3], dnl Linux on 64bit s390x (big endian), glibc, gcc
[x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64
+ [aarch64-apple-ios*-macabi/clang], [PY_SUPPORT_TIER=3], dnl MacCatalyst on arm64
+ [x86_64-apple-ios*-macabi/clang], [PY_SUPPORT_TIER=3], dnl MacCatalyst on x86_64
[aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64
[aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64
+ [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64
+ [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl tvOS on ARM64
+ [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl watchOS Simulator on arm64
+ [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl watchOS on ARM64
+ [aarch64-apple-xros*-simulator/clang], [PY_SUPPORT_TIER=3], dnl visionOS Simulator on arm64
+ [aarch64-apple-xros*/clang], [PY_SUPPORT_TIER=3], dnl visionOS on ARM64
[aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64
[x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64
@@ -1536,7 +1810,7 @@ then
case $ac_sys_system in
Darwin)
LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';;
- iOS)
+ iOS|tvOS|watchOS|visionOS)
LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';;
*)
AC_MSG_ERROR([Unknown platform for framework build]);;
@@ -1601,7 +1875,7 @@ if test $enable_shared = "yes"; then
BLDLIBRARY='-L. -lpython$(LDVERSION)'
RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}}
;;
- iOS)
+ iOS|tvOS|watchOS|visionOS)
LDLIBRARY='libpython$(LDVERSION).dylib'
;;
AIX*)
@@ -3470,7 +3744,7 @@ then
BLDSHARED="$LDSHARED"
fi
;;
- iOS/*)
+ iOS/*|tvOS/*|watchOS/*|visionOS/*)
LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)'
LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)'
BLDSHARED="$LDSHARED"
@@ -3594,7 +3868,7 @@ then
Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";;
Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";;
# -u libsys_s pulls in all symbols in libsys
- Darwin/*|iOS/*)
+ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*)
LINKFORSHARED="$extra_undefs -framework CoreFoundation"
# Issue #18075: the default maximum stack size (8MBytes) is too
@@ -3618,7 +3892,7 @@ then
LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)'
fi
LINKFORSHARED="$LINKFORSHARED"
- elif test $ac_sys_system = "iOS"; then
+ elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS"; then
LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)'
fi
;;
@@ -4106,7 +4380,7 @@ AS_VAR_IF([have_libffi], [yes], [
dnl when do we need USING_APPLE_OS_LIBFFI?
ctypes_malloc_closure=yes
],
- [iOS], [
+ [iOS|tvOS|watchOS|visionOS], [
ctypes_malloc_closure=yes
],
[sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])]
@@ -5215,9 +5489,9 @@ fi
# checks for library functions
AC_CHECK_FUNCS([ \
accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \
- copy_file_range ctermid dladdr dup dup3 execv explicit_bzero explicit_memset \
+ copy_file_range ctermid dladdr dup dup3 explicit_bzero explicit_memset \
faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \
- fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \
+ fpathconf fstatat ftime ftruncate futimens futimes futimesat \
gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \
getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \
getpeername getpgid getpid getppid getpriority _getpty \
@@ -5225,8 +5499,7 @@ AC_CHECK_FUNCS([ \
getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \
lockf lstat lutimes madvise mbrtowc memrchr mkdirat mkfifo mkfifoat \
mknod mknodat mktime mmap mremap nice openat opendir pathconf pause pipe \
- pipe2 plock poll posix_fadvise posix_fallocate posix_openpt posix_spawn posix_spawnp \
- posix_spawn_file_actions_addclosefrom_np \
+ pipe2 plock poll posix_fadvise posix_fallocate posix_openpt \
pread preadv preadv2 process_vm_readv \
pthread_cond_timedwait_relative_np pthread_condattr_setclock pthread_init \
pthread_kill pthread_get_name_np pthread_getname_np pthread_set_name_np
@@ -5236,7 +5509,7 @@ AC_CHECK_FUNCS([ \
sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \
sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \
setitimer setlocale setpgid setpgrp setpriority setregid setresgid \
- setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \
+ setresuid setreuid setsid setuid setvbuf shutdown sigaction \
sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \
sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \
sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \
@@ -5251,12 +5524,20 @@ if test "$MACHDEP" != linux; then
AC_CHECK_FUNCS([lchmod])
fi
-# iOS defines some system methods that can be linked (so they are
+# iOS/tvOS/watchOS/visionOS define some system methods that can be linked (so they are
# found by configure), but either raise a compilation error (because the
# header definition prevents usage - autoconf doesn't use the headers), or
# raise an error if used at runtime. Force these symbols off.
-if test "$ac_sys_system" != "iOS" ; then
- AC_CHECK_FUNCS([getentropy getgroups system])
+if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then
+ AC_CHECK_FUNCS([ getentropy getgroups system ])
+fi
+
+# tvOS/watchOS have some additional methods that can be found, but not used.
+if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then
+ AC_CHECK_FUNCS([ \
+ execv fork fork1 posix_spawn posix_spawnp posix_spawn_file_actions_addclosefrom_np \
+ sigaltstack \
+ ])
fi
AC_CHECK_DECL([dirfd],
@@ -5539,20 +5820,22 @@ PY_CHECK_FUNC([setgroups], [
])
# check for openpty, login_tty, and forkpty
-
-AC_CHECK_FUNCS([openpty], [],
- [AC_CHECK_LIB([util], [openpty],
- [AC_DEFINE([HAVE_OPENPTY]) LIBS="$LIBS -lutil"],
- [AC_CHECK_LIB([bsd], [openpty],
- [AC_DEFINE([HAVE_OPENPTY]) LIBS="$LIBS -lbsd"])])])
-AC_SEARCH_LIBS([login_tty], [util],
- [AC_DEFINE([HAVE_LOGIN_TTY], [1], [Define to 1 if you have the `login_tty' function.])]
-)
-AC_CHECK_FUNCS([forkpty], [],
- [AC_CHECK_LIB([util], [forkpty],
- [AC_DEFINE([HAVE_FORKPTY]) LIBS="$LIBS -lutil"],
- [AC_CHECK_LIB([bsd], [forkpty],
- [AC_DEFINE([HAVE_FORKPTY]) LIBS="$LIBS -lbsd"])])])
+# tvOS/watchOS have functions for tty, but can't use them
+if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then
+ AC_CHECK_FUNCS([openpty], [],
+ [AC_CHECK_LIB([util], [openpty],
+ [AC_DEFINE([HAVE_OPENPTY]) LIBS="$LIBS -lutil"],
+ [AC_CHECK_LIB([bsd], [openpty],
+ [AC_DEFINE([HAVE_OPENPTY]) LIBS="$LIBS -lbsd"])])])
+ AC_SEARCH_LIBS([login_tty], [util],
+ [AC_DEFINE([HAVE_LOGIN_TTY], [1], [Define to 1 if you have the `login_tty' function.])]
+ )
+ AC_CHECK_FUNCS([forkpty], [],
+ [AC_CHECK_LIB([util], [forkpty],
+ [AC_DEFINE([HAVE_FORKPTY]) LIBS="$LIBS -lutil"],
+ [AC_CHECK_LIB([bsd], [forkpty],
+ [AC_DEFINE([HAVE_FORKPTY]) LIBS="$LIBS -lbsd"])])])
+fi
# check for long file support functions
AC_CHECK_FUNCS([fseek64 fseeko fstatvfs ftell64 ftello statvfs])
@@ -5591,10 +5874,10 @@ AC_CHECK_FUNCS([clock_getres], [], [
])
])
-# On Android and iOS, clock_settime can be linked (so it is found by
+# On Android, iOS, tvOS, watchOS, and visionOS, clock_settime can be linked (so it is found by
# configure), but when used in an unprivileged process, it crashes rather than
# returning an error. Force the symbol off.
-if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS"
+if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS"
then
AC_CHECK_FUNCS([clock_settime], [], [
AC_CHECK_LIB([rt], [clock_settime], [
@@ -5752,7 +6035,7 @@ int main(void)
[ac_cv_buggy_getaddrinfo=no],
[ac_cv_buggy_getaddrinfo=yes],
[
-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then
+if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS" || test "$ac_sys_system" = "tvOS" || test "$ac_sys_system" = "watchOS" || test "$ac_sys_system" = "visionOS"; then
ac_cv_buggy_getaddrinfo="no"
elif test "${enable_ipv6+set}" = set; then
ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6"
@@ -6345,8 +6628,8 @@ if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MA
LIBPYTHON="\$(BLDLIBRARY)"
fi
-# On iOS the shared libraries must be linked with the Python framework
-if test "$ac_sys_system" = "iOS"; then
+# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework
+if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then
MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)"
fi
@@ -7005,7 +7288,7 @@ AC_MSG_NOTICE([checking for device files])
dnl NOTE: Inform user how to proceed with files when cross compiling.
dnl Some cross-compile builds are predictable; they won't ever
dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly.
-if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then
+if test "$ac_sys_system" = "Linux-android" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS" ; then
ac_cv_file__dev_ptmx=no
ac_cv_file__dev_ptc=no
else
@@ -7307,7 +7590,7 @@ AC_ARG_WITH([ensurepip],
AS_CASE([$ac_sys_system],
[Emscripten], [with_ensurepip=no],
[WASI], [with_ensurepip=no],
- [iOS], [with_ensurepip=no],
+ [iOS|tvOS|watchOS|visionOS], [with_ensurepip=no],
[with_ensurepip=upgrade]
)
])
@@ -7694,7 +7977,7 @@ case "$ac_sys_system" in
SunOS*) _PYTHREAD_NAME_MAXLEN=31;;
NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268
Darwin) _PYTHREAD_NAME_MAXLEN=63;;
- iOS) _PYTHREAD_NAME_MAXLEN=63;;
+ iOS|tvOS|watchOS|visionOS) _PYTHREAD_NAME_MAXLEN=63;;
FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268
OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268
*) _PYTHREAD_NAME_MAXLEN=;;
@@ -7719,18 +8002,22 @@ AS_CASE([$ac_sys_system],
[VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])],
dnl The _scproxy module is available on macOS
[Darwin], [],
- [iOS], [
+ [iOS|tvOS|watchOS|visionOS], [
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
+ dnl subprocess is however supported for Mac Catalyst
+ case "$_host_device" in
+ macabi) ;;
+ *) PY_STDLIB_MOD_SET_NA([_posixsubprocess]) ;;
+ esac
PY_STDLIB_MOD_SET_NA(
[_curses],
[_curses_panel],
[_gdbm],
[_multiprocessing],
[_posixshmem],
- [_posixsubprocess],
[_scproxy],
[_tkinter],
[grp],
diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in
index c3e261ecd9eff7..26ef7a95de4a1c 100644
--- a/iOS/Resources/Info.plist.in
+++ b/iOS/Resources/Info.plist.in
@@ -17,13 +17,13 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- @VERSION@
+ %VERSION%
CFBundleLongVersionString
%VERSION%, (c) 2001-2024 Python Software Foundation.
CFBundleSignature
????
CFBundleVersion
- 1
+ %VERSION%
CFBundleSupportedPlatforms
iPhoneOS
diff --git a/iOS/testbed/Python.xcframework/Info.plist b/iOS/testbed/Python.xcframework/Info.plist
index c6418de6e74a4e..7b32df19ab4ffc 100644
--- a/iOS/testbed/Python.xcframework/Info.plist
+++ b/iOS/testbed/Python.xcframework/Info.plist
@@ -4,6 +4,23 @@
AvailableLibraries
+
+ BinaryPath
+ Python.framework/Versions/Latest/Python
+ LibraryIdentifier
+ ios-arm64_x86_64-maccatalyst
+ LibraryPath
+ python.framework
+ SupportedArchitectures
+
+ x86_64
+ arm64
+
+ SupportedPlatform
+ ios
+ SupportedPlatformVariant
+ maccatalyst
+
BinaryPath
Python.framework/Python
diff --git a/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README
new file mode 100644
index 00000000000000..b39f40980bc874
--- /dev/null
+++ b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README
@@ -0,0 +1,4 @@
+This directory is intentionally empty.
+
+It is a placeholder slice for the XCFramework on Mac Catalyst,
+to install or copy your built framework to.
diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py
index c05497ede3aa61..1c42b07aebde75 100644
--- a/iOS/testbed/__main__.py
+++ b/iOS/testbed/__main__.py
@@ -28,6 +28,15 @@
r"\s+\(Python\)\s" # Logger name
)
+# Prefix: 2025-04-27 21:43:38.530606-0500 iOSTestbed[96892:48053672]
+CATALYST_LOG_PREFIX_REGEX = re.compile(
+ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD
+ r"\s+\d{2}:\d{2}:\d{2}\.\d{6}" # HH:MM:SS.ssssss (microseconds, 6 digits)
+ r"[-+]\d{4}" # Timezone offset like -0500
+ r"\s+iOSTestbed\[\d+:\d+\] " # iOSTestbed[ProcessID:ThreadID] (both numbers), then a space
+)
+
+
# Work around a bug involving sys.exit and TaskGroups
# (https://github.com/python/cpython/issues/101515).
@@ -127,7 +136,7 @@ async def async_check_output(*args, **kwargs):
async def select_simulator_device():
# List the testing simulators, in JSON format
raw_json = await async_check_output(
- "xcrun", "simctl", "--set", "testing", "list", "-j"
+ "xcrun", "simctl", "list", "-j"
)
json_data = json.loads(raw_json)
@@ -243,9 +252,13 @@ async def log_stream_task(initial_devices, lock):
sys.stdout.flush()
-async def xcode_test(location, simulator, verbose):
+async def xcode_test(location, simulator, verbose, catalyst):
# Run the test suite on the named simulator
print("Starting xcodebuild...", flush=True)
+ if catalyst:
+ destination_arg = "platform=macOS,variant=Mac Catalyst"
+ else:
+ destination_arg = f"platform=iOS Simulator,name={simulator}";
args = [
"xcodebuild",
"test",
@@ -254,13 +267,13 @@ async def xcode_test(location, simulator, verbose):
"-scheme",
"iOSTestbed",
"-destination",
- f"platform=iOS Simulator,name={simulator}",
+ destination_arg,
"-resultBundlePath",
str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"),
"-derivedDataPath",
str(location / "DerivedData"),
]
- if not verbose:
+ if not verbose and not catalyst:
args += ["-quiet"]
async with async_process(
@@ -269,8 +282,16 @@ async def xcode_test(location, simulator, verbose):
stderr=subprocess.STDOUT,
) as process:
while line := (await process.stdout.readline()).decode(*DECODE_ARGS):
- sys.stdout.write(line)
- sys.stdout.flush()
+ # For Mac Catalyst, the *actual* logs are streamed here. Only stream
+ # things that does NOT come from the process when verbose.
+ if catalyst:
+ if CATALYST_LOG_PREFIX_REGEX.match(line) or verbose:
+ line = CATALYST_LOG_PREFIX_REGEX.sub("", line)
+ sys.stdout.write(line)
+ sys.stdout.flush()
+ else:
+ sys.stdout.write(line)
+ sys.stdout.flush()
status = await asyncio.wait_for(process.wait(), timeout=1)
exit(status)
@@ -281,20 +302,31 @@ def clone_testbed(
target: Path,
framework: Path,
apps: list[Path],
+ catalyst: bool,
) -> None:
if target.exists():
print(f"{target} already exists; aborting without creating project.")
sys.exit(10)
if framework is None:
- if not (
- source / "Python.xcframework/ios-arm64_x86_64-simulator/bin"
- ).is_dir():
- print(
- f"The testbed being cloned ({source}) does not contain "
- f"a simulator framework. Re-run with --framework"
- )
- sys.exit(11)
+ if catalyst:
+ if not (
+ source / "Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework/Versions"
+ ).is_dir():
+ print(
+ f"The testbed being cloned ({source}) does not contain "
+ f"a Mac Catalyst framework. Re-run with --framework"
+ )
+ sys.exit(11)
+ else:
+ if not (
+ source / "Python.xcframework/ios-arm64_x86_64-simulator/bin"
+ ).is_dir():
+ print(
+ f"The testbed being cloned ({source}) does not contain "
+ f"a simulator framework. Re-run with --framework"
+ )
+ sys.exit(11)
else:
if not framework.is_dir():
print(f"{framework} does not exist.")
@@ -305,7 +337,7 @@ def clone_testbed(
):
print(
f"{framework} is not an XCframework, "
- f"or a simulator slice of a framework build."
+ f"or a simulator / Catalyst slice of a framework build."
)
sys.exit(13)
@@ -315,7 +347,10 @@ def clone_testbed(
print(" done")
xc_framework_path = target / "Python.xcframework"
- sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator"
+ if catalyst:
+ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-maccatalyst"
+ else:
+ sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator"
if framework is not None:
if framework.suffix == ".xcframework":
print(" Installing XCFramework...", end="", flush=True)
@@ -328,7 +363,7 @@ def clone_testbed(
)
print(" done")
else:
- print(" Installing simulator framework...", end="", flush=True)
+ print(" Installing simulator/catalyst framework...", end="", flush=True)
if sim_framework_path.is_dir():
shutil.rmtree(sim_framework_path)
else:
@@ -360,7 +395,7 @@ def clone_testbed(
sim_framework_path.is_symlink()
and not sim_framework_path.readlink().is_absolute()
):
- print(" Rewriting symlink to simulator framework...", end="", flush=True)
+ print(" Rewriting symlink to simulator/catalyst framework...", end="", flush=True)
# Simulator framework is a relative symlink. Rewrite the symlink
# relative to the new location.
orig_sim_framework_path = (
@@ -401,42 +436,52 @@ def update_plist(testbed_path, args):
plistlib.dump(info, f)
-async def run_testbed(simulator: str | None, args: list[str], verbose: bool=False):
+async def run_testbed(simulator: str | None, args: list[str], catalyst: bool, verbose: bool=False):
location = Path(__file__).parent
print("Updating plist...", end="", flush=True)
update_plist(location, args)
print(" done.", flush=True)
- if simulator is None:
- simulator = await select_simulator_device()
- print(f"Running test on {simulator}", flush=True)
-
- # We need to get an exclusive lock on simulator creation, to avoid issues
- # with multiple simulators starting and being unable to tell which
- # simulator is due to which testbed instance. See
- # https://github.com/python/cpython/issues/130294 for details. Wait up to
- # 10 minutes for a simulator to boot.
- print("Obtaining lock on simulator creation...", flush=True)
- simulator_lock = SimulatorLock(timeout=10*60)
- await simulator_lock.acquire()
- print("Simulator lock acquired.", flush=True)
-
- # Get the list of devices that are booted at the start of the test run.
- # The simulator started by the test suite will be detected as the new
- # entry that appears on the device list.
- initial_devices = await list_devices()
-
- try:
- async with asyncio.TaskGroup() as tg:
- tg.create_task(log_stream_task(initial_devices, simulator_lock))
- tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose))
- except* MySystemExit as e:
- raise SystemExit(*e.exceptions[0].args) from None
- except* subprocess.CalledProcessError as e:
- # Extract it from the ExceptionGroup so it can be handled by `main`.
- raise e.exceptions[0]
- finally:
- simulator_lock.release()
+ if not catalyst:
+ if simulator is None:
+ simulator = await select_simulator_device()
+ print(f"Running test on {simulator}", flush=True)
+
+ # We need to get an exclusive lock on simulator creation, to avoid issues
+ # with multiple simulators starting and being unable to tell which
+ # simulator is due to which testbed instance. See
+ # https://github.com/python/cpython/issues/130294 for details. Wait up to
+ # 10 minutes for a simulator to boot.
+ print("Obtaining lock on simulator creation...", flush=True)
+ simulator_lock = SimulatorLock(timeout=10*60)
+ await simulator_lock.acquire()
+ print("Simulator lock acquired.", flush=True)
+
+ # Get the list of devices that are booted at the start of the test run.
+ # The simulator started by the test suite will be detected as the new
+ # entry that appears on the device list.
+ initial_devices = await list_devices()
+
+ try:
+ async with asyncio.TaskGroup() as tg:
+ tg.create_task(log_stream_task(initial_devices, simulator_lock))
+ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose, catalyst=False))
+ except* MySystemExit as e:
+ raise SystemExit(*e.exceptions[0].args) from None
+ except* subprocess.CalledProcessError as e:
+ # Extract it from the ExceptionGroup so it can be handled by `main`.
+ raise e.exceptions[0]
+ finally:
+ simulator_lock.release()
+ else:
+ try:
+ async with asyncio.TaskGroup() as tg:
+ tg.create_task(xcode_test(location, simulator="", verbose=verbose, catalyst=True))
+ except* MySystemExit as e:
+ raise SystemExit(*e.exceptions[0].args) from None
+ except* subprocess.CalledProcessError as e:
+ # Extract it from the ExceptionGroup so it can be handled by `main`.
+ raise e.exceptions[0]
def main():
@@ -448,6 +493,12 @@ def main():
subcommands = parser.add_subparsers(dest="subcommand")
+ parser.add_argument(
+ "--catalyst",
+ action="store_true",
+ help="Run or clone the testbed for a Mac Catalyst build.",
+ )
+
clone = subcommands.add_parser(
"clone",
description=(
@@ -514,11 +565,16 @@ def main():
target=Path(context.location).resolve(),
framework=Path(context.framework).resolve() if context.framework else None,
apps=[Path(app) for app in context.apps],
+ catalyst = context.catalyst
)
elif context.subcommand == "run":
if test_args:
+ if context.catalyst:
+ expected_location = "Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework"
+ else:
+ expected_location = "Python.xcframework/ios-arm64_x86_64-simulator/bin"
if not (
- Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin"
+ Path(__file__).parent / expected_location
).is_dir():
print(
f"Testbed does not contain a compiled iOS framework. Use "
@@ -532,6 +588,7 @@ def main():
simulator=context.simulator,
verbose=context.verbose,
args=test_args,
+ catalyst=context.catalyst
)
)
else:
diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj
index c7d63909ee2453..ace08f7efd97fc 100644
--- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj
+++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj
@@ -70,6 +70,7 @@
607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = ""; };
608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; };
608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; };
+ EE325B2A2DBE97CD000142D0 /* iOSTestbed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iOSTestbed.entitlements; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -115,6 +116,7 @@
607A66142B0EFA380010BFC8 /* iOSTestbed */ = {
isa = PBXGroup;
children = (
+ EE325B2A2DBE97CD000142D0 /* iOSTestbed.entitlements */,
608619552CB7819B00F46182 /* app */,
608619532CB77BA900F46182 /* app_packages */,
607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */,
@@ -262,7 +264,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n";
+ shellScript = "set -e\n\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n mkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-maccatalyst\" ]; then\n mkdir -p \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib\"\n echo \"Installing Python modules for Mac Catalyst\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework/Versions/3.14/lib/\" \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib/\"\nelse\n mkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\"\nfi\n";
showEnvVarsInLog = 0;
};
607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = {
@@ -282,7 +284,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n";
+ shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-maccatalyst\" ]; then\n PYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib\")\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/app_packages\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/app\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n \nelse\n PYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\n echo \"Install Python $PYTHON_VER standard library extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\n done\n echo \"Install app package extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\n done\n echo \"Install app extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\n done\n\n # Clean up dylib template \n rm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\n echo \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\n find \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -362,6 +364,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_HARDENED_RUNTIME = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -381,6 +384,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -423,6 +427,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -436,6 +441,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
@@ -449,9 +455,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CODE_SIGN_ENTITLEMENTS = iOSTestbed/iOSTestbed.entitlements;
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
+ ENABLE_HARDENED_RUNTIME = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
INFOPLIST_FILE = "iOSTestbed/iOSTestbed-Info.plist";
@@ -468,6 +477,9 @@
MARKETING_VERSION = 3.13.0a1;
PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed;
PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -479,9 +491,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CODE_SIGN_ENTITLEMENTS = iOSTestbed/iOSTestbed.entitlements;
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
+ ENABLE_HARDENED_RUNTIME = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
@@ -499,6 +514,9 @@
MARKETING_VERSION = 3.13.0a1;
PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed;
PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -509,9 +527,10 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = 3HEZE76D99;
+ DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@@ -529,9 +548,10 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = 3HEZE76D99;
+ DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
diff --git a/iOS/testbed/iOSTestbed/iOSTestbed.entitlements b/iOS/testbed/iOSTestbed/iOSTestbed.entitlements
new file mode 100644
index 00000000000000..8cc185af8d78a2
--- /dev/null
+++ b/iOS/testbed/iOSTestbed/iOSTestbed.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.cs.disable-library-validation
+
+
+
diff --git a/tvOS/README.rst b/tvOS/README.rst
new file mode 100644
index 00000000000000..1f793252caf627
--- /dev/null
+++ b/tvOS/README.rst
@@ -0,0 +1,108 @@
+=====================
+Python on tvOS README
+=====================
+
+:Authors:
+ Russell Keith-Magee (2023-11)
+
+This document provides a quick overview of some tvOS specific features in the
+Python distribution.
+
+Compilers for building on tvOS
+==============================
+
+Building for tvOS requires the use of Apple's Xcode tooling. It is strongly
+recommended that you use the most recent stable release of Xcode, on the
+most recently released macOS.
+
+tvOS specific arguments to configure
+===================================
+
+* ``--enable-framework[=DIR]``
+
+ This argument specifies the location where the Python.framework will
+ be installed.
+
+* ``--with-framework-name=NAME``
+
+ Specify the name for the python framework, defaults to ``Python``.
+
+
+Building and using Python on tvOS
+=================================
+
+ABIs and Architectures
+----------------------
+
+tvOS apps can be deployed on physical devices, and on the tvOS simulator.
+Although the API used on these devices is identical, the ABI is different - you
+need to link against different libraries for an tvOS device build
+(``appletvos``) or an tvOS simulator build (``appletvsimulator``). Apple uses
+the XCframework format to allow specifying a single dependency that supports
+multiple ABIs. An XCframework is a wrapper around multiple ABI-specific
+frameworks.
+
+tvOS can also support different CPU architectures within each ABI. At present,
+there is only a single support ed architecture on physical devices - ARM64.
+However, the *simulator* supports 2 architectures - ARM64 (for running on Apple
+Silicon machines), and x86_64 (for running on older Intel-based machines.)
+
+To support multiple CPU architectures on a single platform, Apple uses a "fat
+binary" format - a single physical file that contains support for multiple
+architectures.
+
+How do I build Python for tvOS?
+-------------------------------
+
+The Python build system will build a ``Python.framework`` that supports a
+*single* ABI with a *single* architecture. If you want to use Python in an tvOS
+project, you need to:
+
+1. Produce multiple ``Python.framework`` builds, one for each ABI and architecture;
+2. Merge the binaries for each architecture on a given ABI into a single "fat" binary;
+3. Merge the "fat" frameworks for each ABI into a single XCframework.
+
+tvOS builds of Python *must* be constructed as framework builds. To support this,
+you must provide the ``--enable-framework`` flag when configuring the build.
+
+The build also requires the use of cross-compilation. The commands for building
+Python for tvOS will look somethign like::
+
+ $ ./configure \
+ --enable-framework=/path/to/install \
+ --host=aarch64-apple-tvos \
+ --build=aarch64-apple-darwin \
+ --with-build-python=/path/to/python.exe
+ $ make
+ $ make install
+
+In this invocation:
+
+* ``/path/to/install`` is the location where the final Python.framework will be
+ output.
+
+* ``--host`` is the architecture and ABI that you want to build, in GNU compiler
+ triple format. This will be one of:
+
+ - ``aarch64-apple-tvos`` for ARM64 tvOS devices.
+ - ``aarch64-apple-tvos-simulator`` for the tvOS simulator running on Apple
+ Silicon devices.
+ - ``x86_64-apple-tvos-simulator`` for the tvOS simulator running on Intel
+ devices.
+
+* ``--build`` is the GNU compiler triple for the machine that will be running
+ the compiler. This is one of:
+
+ - ``aarch64-apple-darwin`` for Apple Silicon devices.
+ - ``x86_64-apple-darwin`` for Intel devices.
+
+* ``/path/to/python.exe`` is the path to a Python binary on the machine that
+ will be running the compiler. This is needed because the Python compilation
+ process involves running some Python code. On a normal desktop build of
+ Python, you can compile a python interpreter and then use that interpreter to
+ run Python code. However, the binaries produced for tvOS won't run on macOS, so
+ you need to provide an external Python interpreter. This interpreter must be
+ the version as the Python that is being compiled.
+
+Using a framework-based Python on tvOS
+======================================
diff --git a/tvOS/Resources/Info.plist.in b/tvOS/Resources/Info.plist.in
new file mode 100644
index 00000000000000..ab3050804b8c58
--- /dev/null
+++ b/tvOS/Resources/Info.plist.in
@@ -0,0 +1,34 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ Python
+ CFBundleGetInfoString
+ Python Runtime and Library
+ CFBundleIdentifier
+ @PYTHONFRAMEWORKIDENTIFIER@
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Python
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ %VERSION%
+ CFBundleLongVersionString
+ %VERSION%, (c) 2001-2024 Python Software Foundation.
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ CFBundleSupportedPlatforms
+
+ tvOS
+
+ MinimumOSVersion
+ @TVOS_DEPLOYMENT_TARGET@
+
+
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-ar b/tvOS/Resources/bin/arm64-apple-tvos-ar
new file mode 100755
index 00000000000000..e302748a13c8b1
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvos${TVOS_SDK_VERSION} ar "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-clang b/tvOS/Resources/bin/arm64-apple-tvos-clang
new file mode 100755
index 00000000000000..bef66ed852e1dd
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-clang++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++
new file mode 100755
index 00000000000000..04ca4df9ff00e5
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-cpp b/tvOS/Resources/bin/arm64-apple-tvos-cpp
new file mode 100755
index 00000000000000..cb797b5a5308cd
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos -E "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar
new file mode 100755
index 00000000000000..87ef5015aae9b4
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang
new file mode 100755
index 00000000000000..729f3756fbcab2
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++
new file mode 100755
index 00000000000000..f98b36a6c8f9b6
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos-simulator "$@"
diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp
new file mode 100755
index 00000000000000..40555262ec280e
--- /dev/null
+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator -E "$@"
diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar
new file mode 100755
index 00000000000000..87ef5015aae9b4
--- /dev/null
+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@"
diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang
new file mode 100755
index 00000000000000..27b93b55bd8b6d
--- /dev/null
+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator "$@"
diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++
new file mode 100755
index 00000000000000..df083314351e96
--- /dev/null
+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target x86_64-apple-tvos-simulator "$@"
diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp
new file mode 100755
index 00000000000000..ad0c98ac52c312
--- /dev/null
+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator -E "$@"
diff --git a/tvOS/Resources/dylib-Info-template.plist b/tvOS/Resources/dylib-Info-template.plist
new file mode 100644
index 00000000000000..a20d476fa7b552
--- /dev/null
+++ b/tvOS/Resources/dylib-Info-template.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+
+ CFBundleIdentifier
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ tvOS
+
+ MinimumOSVersion
+ 9.0
+ CFBundleVersion
+ 1
+
+
diff --git a/tvOS/Resources/pyconfig.h b/tvOS/Resources/pyconfig.h
new file mode 100644
index 00000000000000..4acff2c6051637
--- /dev/null
+++ b/tvOS/Resources/pyconfig.h
@@ -0,0 +1,7 @@
+#ifdef __arm64__
+#include "pyconfig-arm64.h"
+#endif
+
+#ifdef __x86_64__
+#include "pyconfig-x86_64.h"
+#endif
diff --git a/visionOS/Resources/Info.plist.in b/visionOS/Resources/Info.plist.in
new file mode 100644
index 00000000000000..2679440da676fa
--- /dev/null
+++ b/visionOS/Resources/Info.plist.in
@@ -0,0 +1,34 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ Python
+ CFBundleGetInfoString
+ Python Runtime and Library
+ CFBundleIdentifier
+ @PYTHONFRAMEWORKIDENTIFIER@
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Python
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ %VERSION%
+ CFBundleLongVersionString
+ %VERSION%, (c) 2001-2023 Python Software Foundation.
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ %VERSION%
+ CFBundleSupportedPlatforms
+
+ XROS
+
+ MinimumOSVersion
+ @XROS_DEPLOYMENT_TARGET@
+
+
diff --git a/visionOS/Resources/bin/arm64-apple-xros-ar b/visionOS/Resources/bin/arm64-apple-xros-ar
new file mode 100755
index 00000000000000..9fd78a205f3ea1
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xros${XROS_SDK_VERSION} ar "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-clang b/visionOS/Resources/bin/arm64-apple-xros-clang
new file mode 100755
index 00000000000000..9a1a757cbd0943
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xros${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET} "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-clang++ b/visionOS/Resources/bin/arm64-apple-xros-clang++
new file mode 100755
index 00000000000000..f64fcfc11cd87b
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xros${XROS_SDK_VERSION} clang++ -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET} "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-cpp b/visionOS/Resources/bin/arm64-apple-xros-cpp
new file mode 100755
index 00000000000000..d6492eff052f85
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xros${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET} -E "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-simulator-ar b/visionOS/Resources/bin/arm64-apple-xros-simulator-ar
new file mode 100755
index 00000000000000..b202330fb5d4ff
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xrsimulator${XROS_SDK_VERSION} ar "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-simulator-clang b/visionOS/Resources/bin/arm64-apple-xros-simulator-clang
new file mode 100755
index 00000000000000..87b0aba0751dda
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xrsimulator${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-simulator-clang++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-clang++
new file mode 100755
index 00000000000000..c89d48f1cb83fa
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xrsimulator${XROS_SDK_VERSION} clang++ -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator "$@"
diff --git a/visionOS/Resources/bin/arm64-apple-xros-simulator-cpp b/visionOS/Resources/bin/arm64-apple-xros-simulator-cpp
new file mode 100755
index 00000000000000..91b2d6264327ba
--- /dev/null
+++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk xrsimulator clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator -E "$@"
diff --git a/visionOS/Resources/dylib-Info-template.plist b/visionOS/Resources/dylib-Info-template.plist
new file mode 100644
index 00000000000000..e91673b4fbcbc4
--- /dev/null
+++ b/visionOS/Resources/dylib-Info-template.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleExecutable
+
+ CFBundleIdentifier
+
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ XROS
+
+ CFBundleVersion
+ 1
+ MinimumOSVersion
+ 2.0
+ UIDeviceFamily
+
+ 7
+
+
+
diff --git a/visionOS/testbed/Python.xcframework/Info.plist b/visionOS/testbed/Python.xcframework/Info.plist
new file mode 100644
index 00000000000000..e75f76c8d94946
--- /dev/null
+++ b/visionOS/testbed/Python.xcframework/Info.plist
@@ -0,0 +1,43 @@
+
+
+
+
+ AvailableLibraries
+
+
+ BinaryPath
+ Python.framework/Python
+ LibraryIdentifier
+ xros-arm64-simulator
+ LibraryPath
+ Python.framework
+ SupportedArchitectures
+
+ arm64
+
+ SupportedPlatform
+ xros
+ SupportedPlatformVariant
+ simulator
+
+
+ BinaryPath
+ Python.framework/Python
+ LibraryIdentifier
+ xros-arm64
+ LibraryPath
+ Python.framework
+ SupportedArchitectures
+
+ arm64
+
+ SupportedPlatform
+ xros
+
+
+ CFBundlePackageType
+ XFWK
+ XCFrameworkFormatVersion
+ 1.0
+
+
diff --git a/visionOS/testbed/Python.xcframework/xros-arm64-simulator/README b/visionOS/testbed/Python.xcframework/xros-arm64-simulator/README
new file mode 100644
index 00000000000000..7bb0ad4b6baa9c
--- /dev/null
+++ b/visionOS/testbed/Python.xcframework/xros-arm64-simulator/README
@@ -0,0 +1,4 @@
+This directory is intentionally empty.
+
+It should be used as a target for `--enable-framework` when compiling an visionOS simulator
+build for testing purposes (either x86_64 or ARM64).
diff --git a/visionOS/testbed/Python.xcframework/xros-arm64/README b/visionOS/testbed/Python.xcframework/xros-arm64/README
new file mode 100644
index 00000000000000..2fc2112e47757a
--- /dev/null
+++ b/visionOS/testbed/Python.xcframework/xros-arm64/README
@@ -0,0 +1,4 @@
+This directory is intentionally empty.
+
+It should be used as a target for `--enable-framework` when compiling an visionOS on-device
+build for testing purposes.
diff --git a/visionOS/testbed/__main__.py b/visionOS/testbed/__main__.py
new file mode 100644
index 00000000000000..5fcf7d128cead6
--- /dev/null
+++ b/visionOS/testbed/__main__.py
@@ -0,0 +1,512 @@
+import argparse
+import asyncio
+import fcntl
+import json
+import os
+import plistlib
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+from contextlib import asynccontextmanager
+from datetime import datetime
+from pathlib import Path
+
+
+DECODE_ARGS = ("UTF-8", "backslashreplace")
+
+# The system log prefixes each line:
+# 2025-01-17 16:14:29.090 Df visionOSTestbed[23987:1fd393b4] (Python) ...
+# 2025-01-17 16:14:29.090 E visionOSTestbed[23987:1fd393b4] (Python) ...
+
+LOG_PREFIX_REGEX = re.compile(
+ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD
+ r"\s+\d+:\d{2}:\d{2}\.\d+" # HH:MM:SS.sss
+ r"\s+\w+" # Df/E
+ r"\s+visionOSTestbed\[\d+:\w+\]" # Process/thread ID
+ r"\s+\(Python\)\s" # Logger name
+)
+
+
+# Work around a bug involving sys.exit and TaskGroups
+# (https://github.com/python/cpython/issues/101515).
+def exit(*args):
+ raise MySystemExit(*args)
+
+
+class MySystemExit(Exception):
+ pass
+
+
+class SimulatorLock:
+ # An fcntl-based filesystem lock that can be used to ensure that
+ def __init__(self, timeout):
+ self.filename = Path(tempfile.gettempdir()) / "python-visionos-testbed"
+ self.timeout = timeout
+
+ self.fd = None
+
+ async def acquire(self):
+ # Ensure the lockfile exists
+ self.filename.touch(exist_ok=True)
+
+ # Try `timeout` times to acquire the lock file, with a 1 second pause
+ # between each attempt. Report status every 10 seconds.
+ for i in range(0, self.timeout):
+ try:
+ fd = os.open(self.filename, os.O_RDWR | os.O_TRUNC, 0o644)
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except OSError:
+ os.close(fd)
+ if i % 10 == 0:
+ print("... waiting", flush=True)
+ await asyncio.sleep(1)
+ else:
+ self.fd = fd
+ return
+
+ # If we reach the end of the loop, we've exceeded the allowed number of
+ # attempts.
+ raise ValueError("Unable to obtain lock on visionOS simulator creation")
+
+ def release(self):
+ # If a lock is held, release it.
+ if self.fd is not None:
+ # Release the lock.
+ fcntl.flock(self.fd, fcntl.LOCK_UN)
+ os.close(self.fd)
+ self.fd = None
+
+
+# All subprocesses are executed through this context manager so that no matter
+# what happens, they can always be cancelled from another task, and they will
+# always be cleaned up on exit.
+@asynccontextmanager
+async def async_process(*args, **kwargs):
+ process = await asyncio.create_subprocess_exec(*args, **kwargs)
+ try:
+ yield process
+ finally:
+ if process.returncode is None:
+ # Allow a reasonably long time for Xcode to clean itself up,
+ # because we don't want stale emulators left behind.
+ timeout = 10
+ process.terminate()
+ try:
+ await asyncio.wait_for(process.wait(), timeout)
+ except TimeoutError:
+ print(
+ f"Command {args} did not terminate after {timeout} seconds "
+ f" - sending SIGKILL"
+ )
+ process.kill()
+
+ # Even after killing the process we must still wait for it,
+ # otherwise we'll get the warning "Exception ignored in __del__".
+ await asyncio.wait_for(process.wait(), timeout=1)
+
+
+async def async_check_output(*args, **kwargs):
+ async with async_process(
+ *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs
+ ) as process:
+ stdout, stderr = await process.communicate()
+ if process.returncode == 0:
+ return stdout.decode(*DECODE_ARGS)
+ else:
+ raise subprocess.CalledProcessError(
+ process.returncode,
+ args,
+ stdout.decode(*DECODE_ARGS),
+ stderr.decode(*DECODE_ARGS),
+ )
+
+
+# Return a list of UDIDs associated with booted simulators
+async def list_devices():
+ try:
+ # List the testing simulators, in JSON format
+ raw_json = await async_check_output(
+ "xcrun", "simctl", "--set", "testing", "list", "-j"
+ )
+ json_data = json.loads(raw_json)
+
+ # Filter out the booted visionOS simulators
+ return [
+ simulator["udid"]
+ for runtime, simulators in json_data["devices"].items()
+ for simulator in simulators
+ if runtime.split(".")[-1].startswith("xrOS") and simulator["state"] == "Booted"
+ ]
+ except subprocess.CalledProcessError as e:
+ # If there's no ~/Library/Developer/XCTestDevices folder (which is the
+ # case on fresh installs, and in some CI environments), `simctl list`
+ # returns error code 1, rather than an empty list. Handle that case,
+ # but raise all other errors.
+ if e.returncode == 1:
+ return []
+ else:
+ raise
+
+
+async def find_device(initial_devices, lock):
+ while True:
+ new_devices = set(await list_devices()).difference(initial_devices)
+ if len(new_devices) == 0:
+ await asyncio.sleep(1)
+ elif len(new_devices) == 1:
+ udid = new_devices.pop()
+ print(f"{datetime.now():%Y-%m-%d %H:%M:%S}: New test simulator detected")
+ print(f"UDID: {udid}", flush=True)
+ lock.release()
+ return udid
+ else:
+ exit(f"Found more than one new device: {new_devices}")
+
+
+async def log_stream_task(initial_devices, lock):
+ # Wait up to 5 minutes for the build to complete and the simulator to boot.
+ udid = await asyncio.wait_for(find_device(initial_devices, lock), 5 * 60)
+
+ # Stream the visionOS device's logs, filtering out messages that come from the
+ # XCTest test suite (catching NSLog messages from the test method), or
+ # Python itself (catching stdout/stderr content routed to the system log
+ # with config->use_system_logger).
+ args = [
+ "xcrun",
+ "simctl",
+ "--set",
+ "testing",
+ "spawn",
+ udid,
+ "log",
+ "stream",
+ "--style",
+ "compact",
+ "--predicate",
+ (
+ 'senderImagePath ENDSWITH "/visionOSTestbedTests.xctest/visionOSTestbedTests"'
+ ' OR senderImagePath ENDSWITH "/Python.framework/Python"'
+ ),
+ ]
+
+ async with async_process(
+ *args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ ) as process:
+ suppress_dupes = False
+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS):
+ # Strip the prefix from each log line
+ line = LOG_PREFIX_REGEX.sub("", line)
+ # The visionOS log streamer can sometimes lag; when it does, it outputs
+ # a warning about messages being dropped... often multiple times.
+ # Only print the first of these duplicated warnings.
+ if line.startswith("=== Messages dropped "):
+ if not suppress_dupes:
+ suppress_dupes = True
+ sys.stdout.write(line)
+ else:
+ suppress_dupes = False
+ sys.stdout.write(line)
+ sys.stdout.flush()
+
+
+async def xcode_test(location, simulator, verbose):
+ # Run the test suite on the named simulator
+ print("Starting xcodebuild...", flush=True)
+ args = [
+ "xcodebuild",
+ "test",
+ "-project",
+ str(location / "visionOSTestbed.xcodeproj"),
+ "-scheme",
+ "visionOSTestbed",
+ "-destination",
+ f"platform=visionOS Simulator,name={simulator}",
+ "-resultBundlePath",
+ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"),
+ "-derivedDataPath",
+ str(location / "DerivedData"),
+ ]
+ if not verbose:
+ args += ["-quiet"]
+
+ async with async_process(
+ *args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ ) as process:
+ while line := (await process.stdout.readline()).decode(*DECODE_ARGS):
+ sys.stdout.write(line)
+ sys.stdout.flush()
+
+ status = await asyncio.wait_for(process.wait(), timeout=1)
+ exit(status)
+
+
+def clone_testbed(
+ source: Path,
+ target: Path,
+ framework: Path,
+ apps: list[Path],
+) -> None:
+ if target.exists():
+ print(f"{target} already exists; aborting without creating project.")
+ sys.exit(10)
+
+ if framework is None:
+ if not (
+ source / "Python.xcframework/xros-arm64-simulator/bin"
+ ).is_dir():
+ print(
+ f"The testbed being cloned ({source}) does not contain "
+ f"a simulator framework. Re-run with --framework"
+ )
+ sys.exit(11)
+ else:
+ if not framework.is_dir():
+ print(f"{framework} does not exist.")
+ sys.exit(12)
+ elif not (
+ framework.suffix == ".xcframework"
+ or (framework / "Python.framework").is_dir()
+ ):
+ print(
+ f"{framework} is not an XCframework, "
+ f"or a simulator slice of a framework build."
+ )
+ sys.exit(13)
+
+ print("Cloning testbed project:")
+ print(f" Cloning {source}...", end="", flush=True)
+ shutil.copytree(source, target, symlinks=True)
+ print(" done")
+
+ xc_framework_path = target / "Python.xcframework"
+ sim_framework_path = xc_framework_path / "xros-arm64-simulator"
+ if framework is not None:
+ if framework.suffix == ".xcframework":
+ print(" Installing XCFramework...", end="", flush=True)
+ if xc_framework_path.is_dir():
+ shutil.rmtree(xc_framework_path)
+ else:
+ xc_framework_path.unlink(missing_ok=True)
+ xc_framework_path.symlink_to(
+ framework.relative_to(xc_framework_path.parent, walk_up=True)
+ )
+ print(" done")
+ else:
+ print(" Installing simulator framework...", end="", flush=True)
+ if sim_framework_path.is_dir():
+ shutil.rmtree(sim_framework_path)
+ else:
+ sim_framework_path.unlink(missing_ok=True)
+ sim_framework_path.symlink_to(
+ framework.relative_to(sim_framework_path.parent, walk_up=True)
+ )
+ print(" done")
+ else:
+ if (
+ xc_framework_path.is_symlink()
+ and not xc_framework_path.readlink().is_absolute()
+ ):
+ # XCFramework is a relative symlink. Rewrite the symlink relative
+ # to the new location.
+ print(" Rewriting symlink to XCframework...", end="", flush=True)
+ orig_xc_framework_path = (
+ source
+ / xc_framework_path.readlink()
+ ).resolve()
+ xc_framework_path.unlink()
+ xc_framework_path.symlink_to(
+ orig_xc_framework_path.relative_to(
+ xc_framework_path.parent, walk_up=True
+ )
+ )
+ print(" done")
+ elif (
+ sim_framework_path.is_symlink()
+ and not sim_framework_path.readlink().is_absolute()
+ ):
+ print(" Rewriting symlink to simulator framework...", end="", flush=True)
+ # Simulator framework is a relative symlink. Rewrite the symlink
+ # relative to the new location.
+ orig_sim_framework_path = (
+ source
+ / "Python.XCframework"
+ / sim_framework_path.readlink()
+ ).resolve()
+ sim_framework_path.unlink()
+ sim_framework_path.symlink_to(
+ orig_sim_framework_path.relative_to(
+ sim_framework_path.parent, walk_up=True
+ )
+ )
+ print(" done")
+ else:
+ print(" Using pre-existing visionOS framework.")
+
+ for app_src in apps:
+ print(f" Installing app {app_src.name!r}...", end="", flush=True)
+ app_target = target / f"visionOSTestbed/app/{app_src.name}"
+ if app_target.is_dir():
+ shutil.rmtree(app_target)
+ shutil.copytree(app_src, app_target)
+ print(" done")
+
+ print(f"Successfully cloned testbed: {target.resolve()}")
+
+
+def update_plist(testbed_path, args):
+ # Add the test runner arguments to the testbed's Info.plist file.
+ info_plist = testbed_path / "visionOSTestbed" / "visionOSTestbed-Info.plist"
+ with info_plist.open("rb") as f:
+ info = plistlib.load(f)
+
+ info["TestArgs"] = args
+
+ with info_plist.open("wb") as f:
+ plistlib.dump(info, f)
+
+
+async def run_testbed(simulator: str, args: list[str], verbose: bool=False):
+ location = Path(__file__).parent
+ print("Updating plist...", end="", flush=True)
+ update_plist(location, args)
+ print(" done.", flush=True)
+
+ # We need to get an exclusive lock on simulator creation, to avoid issues
+ # with multiple simulators starting and being unable to tell which
+ # simulator is due to which testbed instance. See
+ # https://github.com/python/cpython/issues/130294 for details. Wait up to
+ # 10 minutes for a simulator to boot.
+ print("Obtaining lock on simulator creation...", flush=True)
+ simulator_lock = SimulatorLock(timeout=10*60)
+ await simulator_lock.acquire()
+ print("Simulator lock acquired.", flush=True)
+
+ # Get the list of devices that are booted at the start of the test run.
+ # The simulator started by the test suite will be detected as the new
+ # entry that appears on the device list.
+ initial_devices = await list_devices()
+
+ try:
+ async with asyncio.TaskGroup() as tg:
+ tg.create_task(log_stream_task(initial_devices, simulator_lock))
+ tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose))
+ except* MySystemExit as e:
+ raise SystemExit(*e.exceptions[0].args) from None
+ except* subprocess.CalledProcessError as e:
+ # Extract it from the ExceptionGroup so it can be handled by `main`.
+ raise e.exceptions[0]
+ finally:
+ simulator_lock.release()
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=(
+ "Manages the process of testing a Python project in the visionOS simulator."
+ ),
+ )
+
+ subcommands = parser.add_subparsers(dest="subcommand")
+
+ clone = subcommands.add_parser(
+ "clone",
+ description=(
+ "Clone the testbed project, copying in an visionOS Python framework and"
+ "any specified application code."
+ ),
+ help="Clone a testbed project to a new location.",
+ )
+ clone.add_argument(
+ "--framework",
+ help=(
+ "The location of the XCFramework (or simulator-only slice of an "
+ "XCFramework) to use when running the testbed"
+ ),
+ )
+ clone.add_argument(
+ "--app",
+ dest="apps",
+ action="append",
+ default=[],
+ help="The location of any code to include in the testbed project",
+ )
+ clone.add_argument(
+ "location",
+ help="The path where the testbed will be cloned.",
+ )
+
+ run = subcommands.add_parser(
+ "run",
+ usage="%(prog)s [-h] [--simulator SIMULATOR] -- [ ...]",
+ description=(
+ "Run a testbed project. The arguments provided after `--` will be "
+ "passed to the running visionOS process as if they were arguments to "
+ "`python -m`."
+ ),
+ help="Run a testbed project",
+ )
+ run.add_argument(
+ "--simulator",
+ default="Apple Vision Pro",
+ help="The name of the simulator to use (default: 'Apple Vision Pro')",
+ )
+ run.add_argument(
+ "-v", "--verbose",
+ action="store_true",
+ help="Enable verbose output",
+ )
+
+ try:
+ pos = sys.argv.index("--")
+ testbed_args = sys.argv[1:pos]
+ test_args = sys.argv[pos + 1 :]
+ except ValueError:
+ testbed_args = sys.argv[1:]
+ test_args = []
+
+ context = parser.parse_args(testbed_args)
+
+ if context.subcommand == "clone":
+ clone_testbed(
+ source=Path(__file__).parent.resolve(),
+ target=Path(context.location).resolve(),
+ framework=Path(context.framework).resolve() if context.framework else None,
+ apps=[Path(app) for app in context.apps],
+ )
+ elif context.subcommand == "run":
+ if test_args:
+ if not (
+ Path(__file__).parent / "Python.xcframework/xros-arm64-simulator/bin"
+ ).is_dir():
+ print(
+ f"Testbed does not contain a compiled visionOS framework. Use "
+ f"`python {sys.argv[0]} clone ...` to create a runnable "
+ f"clone of this testbed."
+ )
+ sys.exit(20)
+
+ asyncio.run(
+ run_testbed(
+ simulator=context.simulator,
+ verbose=context.verbose,
+ args=test_args,
+ )
+ )
+ else:
+ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)")
+ print()
+ parser.print_help(sys.stderr)
+ sys.exit(21)
+ else:
+ parser.print_help(sys.stderr)
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj b/visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj
new file mode 100644
index 00000000000000..1928369cac528b
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj
@@ -0,0 +1,581 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66162B0EFA380010BFC8 /* AppDelegate.m */; };
+ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607A66212B0EFA390010BFC8 /* Assets.xcassets */; };
+ 607A66282B0EFA390010BFC8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66272B0EFA390010BFC8 /* main.m */; };
+ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */; };
+ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; };
+ 608619542CB77BA900F46182 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 608619532CB77BA900F46182 /* app_packages */; };
+ 608619562CB7819B00F46182 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 608619552CB7819B00F46182 /* app */; };
+ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; };
+ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 607A660A2B0EFA380010BFC8 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 607A66112B0EFA380010BFC8;
+ remoteInfo = iOSTestbed;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = visionOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 607A66152B0EFA380010BFC8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ 607A66162B0EFA380010BFC8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ 607A66212B0EFA390010BFC8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 607A66272B0EFA390010BFC8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = visionOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = visionOSTestbedTests.m; sourceTree = ""; };
+ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = ""; };
+ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "visionOSTestbed-Info.plist"; sourceTree = ""; };
+ 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; };
+ 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; };
+ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 607A660F2B0EFA380010BFC8 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 607A662A2B0EFA3A0010BFC8 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 607A66092B0EFA380010BFC8 = {
+ isa = PBXGroup;
+ children = (
+ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */,
+ 607A66142B0EFA380010BFC8 /* visionOSTestbed */,
+ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */,
+ 607A66132B0EFA380010BFC8 /* Products */,
+ 607A664F2B0EFFE00010BFC8 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 607A66132B0EFA380010BFC8 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */,
+ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 607A66142B0EFA380010BFC8 /* visionOSTestbed */ = {
+ isa = PBXGroup;
+ children = (
+ 608619552CB7819B00F46182 /* app */,
+ 608619532CB77BA900F46182 /* app_packages */,
+ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */,
+ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */,
+ 607A66152B0EFA380010BFC8 /* AppDelegate.h */,
+ 607A66162B0EFA380010BFC8 /* AppDelegate.m */,
+ 607A66212B0EFA390010BFC8 /* Assets.xcassets */,
+ 607A66272B0EFA390010BFC8 /* main.m */,
+ );
+ path = visionOSTestbed;
+ sourceTree = "";
+ };
+ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */ = {
+ isa = PBXGroup;
+ children = (
+ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */,
+ );
+ path = visionOSTestbedTests;
+ sourceTree = "";
+ };
+ 607A664F2B0EFFE00010BFC8 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 607A66112B0EFA380010BFC8 /* visionOSTestbed */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */;
+ buildPhases = (
+ 607A660E2B0EFA380010BFC8 /* Sources */,
+ 607A660F2B0EFA380010BFC8 /* Frameworks */,
+ 607A66102B0EFA380010BFC8 /* Resources */,
+ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */,
+ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */,
+ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = visionOSTestbed;
+ productName = iOSTestbed;
+ productReference = 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */;
+ buildPhases = (
+ 607A66292B0EFA3A0010BFC8 /* Sources */,
+ 607A662A2B0EFA3A0010BFC8 /* Frameworks */,
+ 607A662B2B0EFA3A0010BFC8 /* Resources */,
+ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */,
+ );
+ name = visionOSTestbedTests;
+ productName = iOSTestbedTests;
+ productReference = 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 607A660A2B0EFA380010BFC8 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastUpgradeCheck = 1500;
+ TargetAttributes = {
+ 607A66112B0EFA380010BFC8 = {
+ CreatedOnToolsVersion = 15.0.1;
+ };
+ 607A662C2B0EFA3A0010BFC8 = {
+ CreatedOnToolsVersion = 15.0.1;
+ TestTargetID = 607A66112B0EFA380010BFC8;
+ };
+ };
+ };
+ buildConfigurationList = 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 607A66092B0EFA380010BFC8;
+ productRefGroup = 607A66132B0EFA380010BFC8 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 607A66112B0EFA380010BFC8 /* visionOSTestbed */,
+ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 607A66102B0EFA380010BFC8 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */,
+ 608619562CB7819B00F46182 /* app in Resources */,
+ 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */,
+ 608619542CB77BA900F46182 /* app_packages in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 607A662B2B0EFA3A0010BFC8 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Install Target Specific Python Standard Library";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-xrsimulator\" ]; then\n echo \"Installing Python modules for xrOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/xros-arm64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for xrOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/xros-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n";
+ showEnvVarsInLog = 0;
+ };
+ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ name = "Prepare Python Binary Modules";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 607A660E2B0EFA380010BFC8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 607A66172B0EFA380010BFC8 /* AppDelegate.m in Sources */,
+ 607A66282B0EFA390010BFC8 /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 607A66292B0EFA3A0010BFC8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 607A66112B0EFA380010BFC8 /* visionOSTestbed */;
+ targetProxy = 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 607A663F2B0EFA3A0010BFC8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = xros;
+ };
+ name = Debug;
+ };
+ 607A66402B0EFA3A0010BFC8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = xros;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 607A66422B0EFA3A0010BFC8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist";
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
+ INFOPLIST_KEY_UIMainStoryboardFile = Main;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 3.13.0a1;
+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "xros xrsimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ TARGETED_DEVICE_FAMILY = 7;
+ XROS_DEPLOYMENT_TARGET = 2.0;
+ };
+ name = Debug;
+ };
+ 607A66432B0EFA3A0010BFC8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist";
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
+ INFOPLIST_KEY_UIMainStoryboardFile = Main;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 3.13.0a1;
+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "xros xrsimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ TARGETED_DEVICE_FAMILY = 7;
+ XROS_DEPLOYMENT_TARGET = 2.0;
+ };
+ name = Release;
+ };
+ 607A66452B0EFA3A0010BFC8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 3HEZE76D99;
+ GENERATE_INFOPLIST_FILE = YES;
+ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "xros xrsimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ TARGETED_DEVICE_FAMILY = 7;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/visionOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/visionOSTestbed";
+ };
+ name = Debug;
+ };
+ 607A66462B0EFA3A0010BFC8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 3HEZE76D99;
+ GENERATE_INFOPLIST_FILE = YES;
+ HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\"";
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTED_PLATFORMS = "xros xrsimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ TARGETED_DEVICE_FAMILY = 7;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/visionOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/visionOSTestbed";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 607A663F2B0EFA3A0010BFC8 /* Debug */,
+ 607A66402B0EFA3A0010BFC8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 607A66422B0EFA3A0010BFC8 /* Debug */,
+ 607A66432B0EFA3A0010BFC8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 607A66452B0EFA3A0010BFC8 /* Debug */,
+ 607A66462B0EFA3A0010BFC8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */;
+}
diff --git a/visionOS/testbed/visionOSTestbed/AppDelegate.h b/visionOS/testbed/visionOSTestbed/AppDelegate.h
new file mode 100644
index 00000000000000..8fe7ec8e64eeb2
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/AppDelegate.h
@@ -0,0 +1,11 @@
+//
+// AppDelegate.h
+// visionOSTestbed
+//
+
+#import
+
+@interface AppDelegate : UIResponder
+
+
+@end
diff --git a/visionOS/testbed/visionOSTestbed/AppDelegate.m b/visionOS/testbed/visionOSTestbed/AppDelegate.m
new file mode 100644
index 00000000000000..b3ff14f7255984
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/AppDelegate.m
@@ -0,0 +1,19 @@
+//
+// AppDelegate.m
+// visionOSTestbed
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ return YES;
+}
+
+@end
diff --git a/visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000000000..eb878970081645
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000000..13613e3ee1a934
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,13 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json b/visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json
new file mode 100644
index 00000000000000..73c00596a7fca3
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/visionOS/testbed/visionOSTestbed/app/README b/visionOS/testbed/visionOSTestbed/app/README
new file mode 100644
index 00000000000000..af22c685f87976
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/app/README
@@ -0,0 +1,7 @@
+This folder can contain any Python application code.
+
+During the build, any binary modules found in this folder will be processed into
+iOS Framework form.
+
+When the test suite runs, this folder will be on the PYTHONPATH, and will be the
+working directory for the test suite.
diff --git a/visionOS/testbed/visionOSTestbed/app_packages/README b/visionOS/testbed/visionOSTestbed/app_packages/README
new file mode 100644
index 00000000000000..42d7fdeb813250
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/app_packages/README
@@ -0,0 +1,7 @@
+This folder can be a target for installing any Python dependencies needed by the
+test suite.
+
+During the build, any binary modules found in this folder will be processed into
+iOS Framework form.
+
+When the test suite runs, this folder will be on the PYTHONPATH.
diff --git a/visionOS/testbed/visionOSTestbed/dylib-Info-template.plist b/visionOS/testbed/visionOSTestbed/dylib-Info-template.plist
new file mode 100644
index 00000000000000..e91673b4fbcbc4
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/dylib-Info-template.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleExecutable
+
+ CFBundleIdentifier
+
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ XROS
+
+ CFBundleVersion
+ 1
+ MinimumOSVersion
+ 2.0
+ UIDeviceFamily
+
+ 7
+
+
+
diff --git a/visionOS/testbed/visionOSTestbed/main.m b/visionOS/testbed/visionOSTestbed/main.m
new file mode 100644
index 00000000000000..2bb491f25c851b
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/main.m
@@ -0,0 +1,16 @@
+//
+// main.m
+// visionOSTestbed
+//
+
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ NSString * appDelegateClassName;
+ @autoreleasepool {
+ appDelegateClassName = NSStringFromClass([AppDelegate class]);
+
+ return UIApplicationMain(argc, argv, nil, appDelegateClassName);
+ }
+}
diff --git a/visionOS/testbed/visionOSTestbed/visionOSTestbed-Info.plist b/visionOS/testbed/visionOSTestbed/visionOSTestbed-Info.plist
new file mode 100644
index 00000000000000..fce9298555da00
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbed/visionOSTestbed-Info.plist
@@ -0,0 +1,56 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ ${PRODUCT_NAME}
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIdentifier
+ org.python.visionOSTestbed
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ TestArgs
+
+ test
+ -uall
+ --single-process
+ --rerun
+ -W
+
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+
+ UIRequiresFullScreen
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m b/visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m
new file mode 100644
index 00000000000000..8f1cb020da2145
--- /dev/null
+++ b/visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m
@@ -0,0 +1,162 @@
+#import
+#import
+
+@interface visionOSTestbedTests : XCTestCase
+
+@end
+
+@implementation visionOSTestbedTests
+
+
+- (void)testPython {
+ const char **argv;
+ int exit_code;
+ int failed;
+ PyStatus status;
+ PyPreConfig preconfig;
+ PyConfig config;
+ PyObject *sys_module;
+ PyObject *sys_path_attr;
+ NSArray *test_args;
+ NSString *python_home;
+ NSString *path;
+ wchar_t *wtmp_str;
+
+ NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
+
+ // Set some other common environment indicators to disable color, as the
+ // Xcode log can't display color. Stdout will report that it is *not* a
+ // TTY.
+ setenv("NO_COLOR", "1", true);
+ setenv("PYTHON_COLORS", "0", true);
+
+ // Arguments to pass into the test suite runner.
+ // argv[0] must identify the process; any subsequent arg
+ // will be handled as if it were an argument to `python -m test`
+ test_args = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TestArgs"];
+ if (test_args == NULL) {
+ NSLog(@"Unable to identify test arguments.");
+ }
+ argv = malloc(sizeof(char *) * ([test_args count] + 1));
+ argv[0] = "visionOSTestbed";
+ for (int i = 1; i < [test_args count]; i++) {
+ argv[i] = [[test_args objectAtIndex:i] UTF8String];
+ }
+ NSLog(@"Test command: %@", test_args);
+
+ // Generate an isolated Python configuration.
+ NSLog(@"Configuring isolated Python...");
+ PyPreConfig_InitIsolatedConfig(&preconfig);
+ PyConfig_InitIsolatedConfig(&config);
+
+ // Configure the Python interpreter:
+ // Enforce UTF-8 encoding for stderr, stdout, file-system encoding and locale.
+ // See https://docs.python.org/3/library/os.html#python-utf-8-mode.
+ preconfig.utf8_mode = 1;
+ // Use the system logger for stdout/err
+ config.use_system_logger = 1;
+ // Don't buffer stdio. We want output to appears in the log immediately
+ config.buffered_stdio = 0;
+ // Don't write bytecode; we can't modify the app bundle
+ // after it has been signed.
+ config.write_bytecode = 0;
+ // Ensure that signal handlers are installed
+ config.install_signal_handlers = 1;
+ // Run the test module.
+ config.run_module = Py_DecodeLocale([[test_args objectAtIndex:0] UTF8String], NULL);
+ // For debugging - enable verbose mode.
+ // config.verbose = 1;
+
+ NSLog(@"Pre-initializing Python runtime...");
+ status = Py_PreInitialize(&preconfig);
+ if (PyStatus_Exception(status)) {
+ XCTFail(@"Unable to pre-initialize Python interpreter: %s", status.err_msg);
+ PyConfig_Clear(&config);
+ return;
+ }
+
+ // Set the home for the Python interpreter
+ python_home = [NSString stringWithFormat:@"%@/python", resourcePath, nil];
+ NSLog(@"PythonHome: %@", python_home);
+ wtmp_str = Py_DecodeLocale([python_home UTF8String], NULL);
+ status = PyConfig_SetString(&config, &config.home, wtmp_str);
+ if (PyStatus_Exception(status)) {
+ XCTFail(@"Unable to set PYTHONHOME: %s", status.err_msg);
+ PyConfig_Clear(&config);
+ return;
+ }
+ PyMem_RawFree(wtmp_str);
+
+ // Read the site config
+ status = PyConfig_Read(&config);
+ if (PyStatus_Exception(status)) {
+ XCTFail(@"Unable to read site config: %s", status.err_msg);
+ PyConfig_Clear(&config);
+ return;
+ }
+
+ NSLog(@"Configure argc/argv...");
+ status = PyConfig_SetBytesArgv(&config, [test_args count], (char**) argv);
+ if (PyStatus_Exception(status)) {
+ XCTFail(@"Unable to configure argc/argv: %s", status.err_msg);
+ PyConfig_Clear(&config);
+ return;
+ }
+
+ NSLog(@"Initializing Python runtime...");
+ status = Py_InitializeFromConfig(&config);
+ if (PyStatus_Exception(status)) {
+ XCTFail(@"Unable to initialize Python interpreter: %s", status.err_msg);
+ PyConfig_Clear(&config);
+ return;
+ }
+
+ sys_module = PyImport_ImportModule("sys");
+ if (sys_module == NULL) {
+ XCTFail(@"Could not import sys module");
+ return;
+ }
+
+ sys_path_attr = PyObject_GetAttrString(sys_module, "path");
+ if (sys_path_attr == NULL) {
+ XCTFail(@"Could not access sys.path");
+ return;
+ }
+
+ // Add the app packages path
+ path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil];
+ NSLog(@"App packages path: %@", path);
+ wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
+ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String]));
+ if (failed) {
+ XCTFail(@"Unable to add app packages to sys.path");
+ return;
+ }
+ PyMem_RawFree(wtmp_str);
+
+ path = [NSString stringWithFormat:@"%@/app", resourcePath, nil];
+ NSLog(@"App path: %@", path);
+ wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
+ failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String]));
+ if (failed) {
+ XCTFail(@"Unable to add app to sys.path");
+ return;
+ }
+ PyMem_RawFree(wtmp_str);
+
+ // Ensure the working directory is the app folder.
+ chdir([path UTF8String]);
+
+ // Start the test suite. Print a separator to differentiate Python startup logs from app logs
+ NSLog(@"---------------------------------------------------------------------------");
+
+ exit_code = Py_RunMain();
+ XCTAssertEqual(exit_code, 0, @"Test suite did not pass");
+
+ NSLog(@"---------------------------------------------------------------------------");
+
+ Py_Finalize();
+}
+
+
+@end
diff --git a/watchOS/README.rst b/watchOS/README.rst
new file mode 100644
index 00000000000000..3522147845222f
--- /dev/null
+++ b/watchOS/README.rst
@@ -0,0 +1,108 @@
+========================
+Python on watchOS README
+========================
+
+:Authors:
+ Russell Keith-Magee (2023-11)
+
+This document provides a quick overview of some watchOS specific features in the
+Python distribution.
+
+Compilers for building on watchOS
+=================================
+
+Building for watchOS requires the use of Apple's Xcode tooling. It is strongly
+recommended that you use the most recent stable release of Xcode, on the
+most recently released macOS.
+
+watchOS specific arguments to configure
+=======================================
+
+* ``--enable-framework[=DIR]``
+
+ This argument specifies the location where the Python.framework will
+ be installed.
+
+* ``--with-framework-name=NAME``
+
+ Specify the name for the python framework, defaults to ``Python``.
+
+
+Building and using Python on watchOS
+====================================
+
+ABIs and Architectures
+----------------------
+
+watchOS apps can be deployed on physical devices, and on the watchOS simulator.
+Although the API used on these devices is identical, the ABI is different - you
+need to link against different libraries for an watchOS device build
+(``watchos``) or an watchOS simulator build (``watchsimulator``). Apple uses the
+XCframework format to allow specifying a single dependency that supports
+multiple ABIs. An XCframework is a wrapper around multiple ABI-specific
+frameworks.
+
+watchOS can also support different CPU architectures within each ABI. At present,
+there is only a single support ed architecture on physical devices - ARM64.
+However, the *simulator* supports 2 architectures - ARM64 (for running on Apple
+Silicon machines), and x86_64 (for running on older Intel-based machines.)
+
+To support multiple CPU architectures on a single platform, Apple uses a "fat
+binary" format - a single physical file that contains support for multiple
+architectures.
+
+How do I build Python for watchOS?
+-------------------------------
+
+The Python build system will build a ``Python.framework`` that supports a
+*single* ABI with a *single* architecture. If you want to use Python in an watchOS
+project, you need to:
+
+1. Produce multiple ``Python.framework`` builds, one for each ABI and architecture;
+2. Merge the binaries for each architecture on a given ABI into a single "fat" binary;
+3. Merge the "fat" frameworks for each ABI into a single XCframework.
+
+watchOS builds of Python *must* be constructed as framework builds. To support this,
+you must provide the ``--enable-framework`` flag when configuring the build.
+
+The build also requires the use of cross-compilation. The commands for building
+Python for watchOS will look somethign like::
+
+ $ ./configure \
+ --enable-framework=/path/to/install \
+ --host=aarch64-apple-watchos \
+ --build=aarch64-apple-darwin \
+ --with-build-python=/path/to/python.exe
+ $ make
+ $ make install
+
+In this invocation:
+
+* ``/path/to/install`` is the location where the final Python.framework will be
+ output.
+
+* ``--host`` is the architecture and ABI that you want to build, in GNU compiler
+ triple format. This will be one of:
+
+ - ``arm64_32-apple-watchos`` for ARM64-32 watchOS devices.
+ - ``aarch64-apple-watchos-simulator`` for the watchOS simulator running on Apple
+ Silicon devices.
+ - ``x86_64-apple-watchos-simulator`` for the watchOS simulator running on Intel
+ devices.
+
+* ``--build`` is the GNU compiler triple for the machine that will be running
+ the compiler. This is one of:
+
+ - ``aarch64-apple-darwin`` for Apple Silicon devices.
+ - ``x86_64-apple-darwin`` for Intel devices.
+
+* ``/path/to/python.exe`` is the path to a Python binary on the machine that
+ will be running the compiler. This is needed because the Python compilation
+ process involves running some Python code. On a normal desktop build of
+ Python, you can compile a python interpreter and then use that interpreter to
+ run Python code. However, the binaries produced for watchOS won't run on macOS, so
+ you need to provide an external Python interpreter. This interpreter must be
+ the version as the Python that is being compiled.
+
+Using a framework-based Python on watchOS
+======================================
diff --git a/watchOS/Resources/Info.plist.in b/watchOS/Resources/Info.plist.in
new file mode 100644
index 00000000000000..e83ddfd2a43509
--- /dev/null
+++ b/watchOS/Resources/Info.plist.in
@@ -0,0 +1,34 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ Python
+ CFBundleGetInfoString
+ Python Runtime and Library
+ CFBundleIdentifier
+ @PYTHONFRAMEWORKIDENTIFIER@
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Python
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ %VERSION%
+ CFBundleLongVersionString
+ %VERSION%, (c) 2001-2023 Python Software Foundation.
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ %VERSION%
+ CFBundleSupportedPlatforms
+
+ watchOS
+
+ MinimumOSVersion
+ @WATCHOS_DEPLOYMENT_TARGET@
+
+
diff --git a/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar b/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar
new file mode 100755
index 00000000000000..dda2b211bd5d5f
--- /dev/null
+++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar "$@"
diff --git a/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang b/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang
new file mode 100755
index 00000000000000..38c3de7f86b094
--- /dev/null
+++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang -target arm64-apple-watchos-simulator "$@"
diff --git a/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang++
new file mode 100755
index 00000000000000..e25acb1a52dae4
--- /dev/null
+++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang++ -target arm64-apple-watchos-simulator "$@"
diff --git a/watchOS/Resources/bin/arm64-apple-watchos-simulator-cpp b/watchOS/Resources/bin/arm64-apple-watchos-simulator-cpp
new file mode 100755
index 00000000000000..0503ed40f64a5a
--- /dev/null
+++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator clang -target arm64-apple-watchos-simulator -E "$@"
diff --git a/watchOS/Resources/bin/arm64_32-apple-watchos-ar b/watchOS/Resources/bin/arm64_32-apple-watchos-ar
new file mode 100755
index 00000000000000..029f9a320736ba
--- /dev/null
+++ b/watchOS/Resources/bin/arm64_32-apple-watchos-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchos${WATCHOS_SDK_VERSION} ar "$@"
diff --git a/watchOS/Resources/bin/arm64_32-apple-watchos-clang b/watchOS/Resources/bin/arm64_32-apple-watchos-clang
new file mode 100755
index 00000000000000..0c6a20795e9b76
--- /dev/null
+++ b/watchOS/Resources/bin/arm64_32-apple-watchos-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchos${WATCHOS_SDK_VERSION} clang -target arm64_32-apple-watchos "$@"
diff --git a/watchOS/Resources/bin/arm64_32-apple-watchos-clang++ b/watchOS/Resources/bin/arm64_32-apple-watchos-clang++
new file mode 100755
index 00000000000000..89da49a1f7862d
--- /dev/null
+++ b/watchOS/Resources/bin/arm64_32-apple-watchos-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchos${WATCHOS_SDK_VERSION} clang++ -target arm64_32-apple-watchos "$@"
diff --git a/watchOS/Resources/bin/arm64_32-apple-watchos-cpp b/watchOS/Resources/bin/arm64_32-apple-watchos-cpp
new file mode 100755
index 00000000000000..1b911273f08ce6
--- /dev/null
+++ b/watchOS/Resources/bin/arm64_32-apple-watchos-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchos${WATCHOS_SDK_VERSION} clang -target arm64_32-apple-watchos -E "$@"
diff --git a/watchOS/Resources/bin/x86_64-apple-watchos-simulator-ar b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-ar
new file mode 100755
index 00000000000000..dda2b211bd5d5f
--- /dev/null
+++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-ar
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar "$@"
diff --git a/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang
new file mode 100755
index 00000000000000..185a8fb22e099e
--- /dev/null
+++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang -target x86_64-apple-watchos-simulator "$@"
diff --git a/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang++
new file mode 100755
index 00000000000000..d1127720bb9b51
--- /dev/null
+++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang++
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang++ -target x86_64-apple-watchos-simulator "$@"
diff --git a/watchOS/Resources/bin/x86_64-apple-watchos-simulator-cpp b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-cpp
new file mode 100755
index 00000000000000..bd436d8a6c3917
--- /dev/null
+++ b/watchOS/Resources/bin/x86_64-apple-watchos-simulator-cpp
@@ -0,0 +1,2 @@
+#!/bin/bash
+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} clang -target x86_64-apple-watchos-simulator -E "$@"
diff --git a/watchOS/Resources/dylib-Info-template.plist b/watchOS/Resources/dylib-Info-template.plist
new file mode 100644
index 00000000000000..6f8c0bc2095955
--- /dev/null
+++ b/watchOS/Resources/dylib-Info-template.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+
+ CFBundleIdentifier
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSupportedPlatforms
+
+ watchOS
+
+ MinimumOSVersion
+ 4.0
+ CFBundleVersion
+ 1
+
+
diff --git a/watchOS/Resources/pyconfig.h b/watchOS/Resources/pyconfig.h
new file mode 100644
index 00000000000000..f842b987b2edba
--- /dev/null
+++ b/watchOS/Resources/pyconfig.h
@@ -0,0 +1,11 @@
+#ifdef __arm64__
+# ifdef __LP64__
+#include "pyconfig-arm64.h"
+# else
+#include "pyconfig-arm64_32.h"
+# endif
+#endif
+
+#ifdef __x86_64__
+#include "pyconfig-x86_64.h"
+#endif