diff --git a/.gitignore b/.gitignore index 7965f6a404368e..b0243685138bac 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,8 @@ Lib/test/data/* /Makefile /Makefile.pre /iOSTestbed.* +/tvOSTestbed.* +/visionOSTestbed.* iOS/Frameworks/ iOS/Resources/Info.plist iOS/testbed/build @@ -81,6 +83,27 @@ iOS/testbed/Python.xcframework/ios-*/Python.framework iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace iOS/testbed/iOSTestbed.xcodeproj/xcuserdata iOS/testbed/iOSTestbed.xcodeproj/xcshareddata +tvOS/testbed/build +tvOS/testbed/Python.xcframework/tvos-*/bin +tvOS/testbed/Python.xcframework/tvos-*/include +tvOS/testbed/Python.xcframework/tvos-*/lib +tvOS/testbed/Python.xcframework/tvos-*/Python.framework +tvOS/testbed/tvOSTestbed.xcodeproj/project.xcworkspace +tvOS/testbed/tvOSTestbed.xcodeproj/xcuserdata +tvOS/testbed/tvOSTestbed.xcodeproj/xcshareddata +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/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..d8a6f28edba39c 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,7 @@ def _get_supported_file_loaders(): """ extension_loaders = [] if hasattr(_imp, 'create_dynamic'): - if sys.platform in {"ios", "tvos", "watchos"}: + if sys.platform in {"ios", "tvos", "watchos", "visionos"}: 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..cad919bc0c4969 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -528,6 +528,78 @@ def ios_ver(system="", release="", model="", is_simulator=False): return IOSVersionInfo(system, release, model, is_simulator) +# 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: + 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: + 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: + return VisionOSVersionInfo(*result) + + return VisionOSVersionInfo(system, release, model, is_simulator) + + def _java_getprop(name, default): """This private helper is deprecated in 3.13 and will be removed in 3.15""" from java.lang import System @@ -727,7 +799,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,14 +963,30 @@ 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, 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"): 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' + def from_subprocess(): """ Fall back to `uname -p` @@ -1058,9 +1146,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() + 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 '' @@ -1350,6 +1444,12 @@ def platform(aliased=False, terse=False): # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": 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..03896a234bf6ef 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -75,7 +75,7 @@ _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"} 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..8594f92c097f07 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, + # Apple mobile platforms require the use of the custom framework loader, # not the ExtensionFileLoader. - if sys.platform == "ios": + if support.is_apple_mobile: extension_loader = "AppleFrameworkLoader" else: extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index b7cd7940eb15b3..32243a49e7a407 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -558,7 +558,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,7 +574,7 @@ 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" has_fork_support = hasattr(os, "fork") and not ( 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_platform.py b/Lib/test/test_platform.py index 719c4feace6544..92a831a9148cc4 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, "") 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/Makefile.pre.in b/Makefile.pre.in index b5703fbe6ae974..651320d7164de5 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,7 +2270,7 @@ 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 \ @@ -2284,11 +2290,71 @@ testios: 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-iOS + $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)" + + # Run the testbed project + $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" run --verbose -- test -uall --single-process --rerun -W + +# 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 visionOS testbed with a non-default framework name."; \ + exit 1;\ + fi + @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \ + 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-visionOS + $(PYTHON_FOR_BUILD) $(srcdir)/visionOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-visionOS)" + + # Run the testbed project + $(PYTHON_FOR_BUILD) "$(XCFOLDER-visionOS)" run --verbose -- test -uall --single-process --rerun -W + +# Run the test suite on the tvOS simulator. Must be run on a macOS machine with +# 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-tvOS:=tvOSTestbed.$(MULTIARCH).$(shell date +%s).$$PPID +.PHONY: testtvos +testtvos: + @if test "$(MACHDEP)" != "tvos"; then \ + echo "Cannot run the tvOS testbed for a non-tvOS build."; \ + exit 1;\ + fi + @if test "$(findstring -appletvsimulator,$(MULTIARCH))" != "-appletvsimulator"; then \ + echo "Cannot run the tvOS testbed for non-simulator builds."; \ + exit 1;\ + fi + @if test $(PYTHONFRAMEWORK) != "Python"; then \ + echo "Cannot run the tvOS testbed with a non-default framework name."; \ + exit 1;\ + fi + @if ! test -d $(PYTHONFRAMEWORKPREFIX); then \ + echo "Cannot find a finalized tvOS Python.framework. Have you run 'make install' to finalize the framework build?"; \ + exit 1;\ + fi + + # Clone the testbed project into the XCFOLDER-tvOS + $(PYTHON_FOR_BUILD) $(srcdir)/tvOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-tvOS)" # Run the testbed project - $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W + $(PYTHON_FOR_BUILD) "$(XCFOLDER-tvOS)" 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 diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index f5cd73bdea8333..6c1863c943b353 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -257,6 +257,32 @@ PLATFORM_TRIPLET=arm64-iphonesimulator # 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..49febd56a37cc0 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* \ @@ -1867,7 +1867,7 @@ case $kernel-$os-$obj in ;; *-eabi*- | *-gnueabi*-) ;; - ios*-simulator- | tvos*-simulator- | watchos*-simulator- ) + ios*-simulator- | tvos*-simulator- | watchos*-simulator- | xros*-simulator-) ;; none--*) # None (no kernel, i.e. freestanding / bare metal), diff --git a/configure b/configure index 884f8a4b0680d9..7c93d36e71729b 100755 --- a/configure +++ b/configure @@ -982,6 +982,10 @@ LDFLAGS CFLAGS CC HAS_XCRUN +EXPORT_XROS_DEPLOYMENT_TARGET +XROS_DEPLOYMENT_TARGET +WATCHOS_DEPLOYMENT_TARGET +TVOS_DEPLOYMENT_TARGET IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET @@ -4116,6 +4120,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 +4210,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 @@ -4212,6 +4225,17 @@ if test -z "$AR"; then 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 @@ -4220,6 +4244,17 @@ if test -z "$CC"; then 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 @@ -4228,6 +4263,17 @@ if test -z "$CPP"; then 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 @@ -4236,6 +4282,17 @@ if test -z "$CXX"; then 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 +4415,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 +4428,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 @@ -4474,6 +4537,51 @@ then : ac_config_files="$ac_config_files iOS/Resources/Info.plist" + ;; + tvOS) : + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" + + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + RESSRCDIR=tvOS/Resources + + 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" + ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 @@ -4485,6 +4593,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 +4650,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 +4669,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; } @@ -4577,6 +4688,8 @@ fi +EXPORT_XROS_DEPLOYMENT_TARGET='#' + if test "$cross_compiling" = yes; then case "$host" in @@ -4614,6 +4727,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 +4889,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 +4960,13 @@ 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 # checks for alternative programs @@ -4810,6 +5007,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 +7386,12 @@ case $ac_sys_system in #( MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : + MULTIARCH="" ;; #( + visionOS) : + MULTIARCH="" ;; #( FreeBSD*) : MULTIARCH="" ;; #( *) : @@ -7199,7 +7412,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 @@ -7250,6 +7463,18 @@ case $host/$ac_cv_cc_name in #( 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 +7911,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 +7977,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 +13799,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 +13932,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 +13956,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 +15733,7 @@ then : ctypes_malloc_closure=yes ;; #( - iOS) : + iOS|tvOS|watchOS|visionOS) : ctypes_malloc_closure=yes ;; #( @@ -19260,12 +19485,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 +19545,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 +19971,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 +20289,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 +20557,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 +20583,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 +24074,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 +24189,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 +24372,7 @@ esac fi done +fi # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" @@ -24406,10 +24638,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 +24958,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 +26980,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 +29851,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 +30361,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 +31310,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,7 +31342,7 @@ case $ac_sys_system in #( ;; #( Darwin) : ;; #( - iOS) : + iOS|tvOS|watchOS|visionOS) : @@ -35272,6 +35504,9 @@ do "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" ;; "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..7ab0609bf8ab3e 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 @@ -420,6 +429,17 @@ if test -z "$AR"; then 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 @@ -428,6 +448,17 @@ if test -z "$CC"; then 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 @@ -436,6 +467,17 @@ if test -z "$CPP"; then 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 @@ -444,6 +486,17 @@ if test -z "$CXX"; then 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 +611,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 +624,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 @@ -670,6 +729,48 @@ AC_ARG_ENABLE([framework], AC_CONFIG_FILES([iOS/Resources/Info.plist]) ;; + tvOS) : + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" + + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + RESSRCDIR=tvOS/Resources + + 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 +779,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 +834,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 +849,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 +863,8 @@ AC_ARG_WITH( ]) AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) +EXPORT_XROS_DEPLOYMENT_TARGET='#' + AC_SUBST([_PYTHON_HOST_PLATFORM]) if test "$cross_compiling" = yes; then case "$host" in @@ -794,6 +900,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 +1053,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 +1120,14 @@ 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]) # checks for alternative programs @@ -979,11 +1161,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 +1362,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 +1386,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] ) @@ -1227,6 +1420,12 @@ AS_CASE([$host/$ac_cv_cc_name], [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [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 +1735,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 +1800,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 +3669,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 +3793,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 +3817,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 +4305,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 +5414,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 +5424,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 +5434,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 +5449,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 +5745,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 +5799,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 +5960,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 +6553,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 +7213,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 +7515,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 +7902,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,7 +7927,7 @@ 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 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/__main__.py b/iOS/testbed/__main__.py index c05497ede3aa61..1146bf3b988cda 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py @@ -127,7 +127,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) diff --git a/tvOS/README.rst b/tvOS/README.rst new file mode 100644 index 00000000000000..57d1f3632e2cbd --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,109 @@ +===================== +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 something like:: + + $ export PATH="$(pwd)/tvOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin" + $ ./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/tvOS/testbed/Python.xcframework/Info.plist b/tvOS/testbed/Python.xcframework/Info.plist new file mode 100644 index 00000000000000..bc07ad6bc45397 --- /dev/null +++ b/tvOS/testbed/Python.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + Python.framework/Python + LibraryIdentifier + tvos-arm64 + LibraryPath + Python.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + tvos + + + BinaryPath + Python.framework/Python + LibraryIdentifier + tvos-arm64_x86_64-simulator + LibraryPath + Python.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + tvos + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/tvOS/testbed/Python.xcframework/tvos-arm64/README b/tvOS/testbed/Python.xcframework/tvos-arm64/README new file mode 100644 index 00000000000000..10a546c15bb512 --- /dev/null +++ b/tvOS/testbed/Python.xcframework/tvos-arm64/README @@ -0,0 +1,5 @@ +This directory is intentionally empty. + +It is used as a destination for --enable-framework when building for AppleTVOS device, or +it could be utilized by the testbed utility to clone a slice here. + diff --git a/tvOS/testbed/Python.xcframework/tvos-arm64_x86_64-simulator/README b/tvOS/testbed/Python.xcframework/tvos-arm64_x86_64-simulator/README new file mode 100644 index 00000000000000..c5fa4037aa127f --- /dev/null +++ b/tvOS/testbed/Python.xcframework/tvos-arm64_x86_64-simulator/README @@ -0,0 +1,5 @@ +This directory is intentionally empty. + +It is used as a destination for --enable-framework when building for AppleTVOS simulator, or +it could be utilized by the testbed utility to clone a slice here. + diff --git a/tvOS/testbed/__main__.py b/tvOS/testbed/__main__.py new file mode 100644 index 00000000000000..989f68794b839f --- /dev/null +++ b/tvOS/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 tvOSTestbed[23987:1fd393b4] (Python) ... +# 2025-01-17 16:14:29.090 E tvOSTestbed[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+tvOSTestbed\[\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-tvos-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 tvOS 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 tvOS simulators + return [ + simulator["udid"] + for runtime, simulators in json_data["devices"].items() + for simulator in simulators + if runtime.split(".")[-1].startswith("tvOS") 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 tvOS 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 "/tvOSTestbedTests.xctest/tvOSTestbedTests"' + ' 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 tvOS 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 / "tvOSTestbed.xcodeproj"), + "-scheme", + "tvOSTestbed", + "-destination", + f"platform=tvOS 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/tvos-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.") + 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 / "tvos-arm64_x86_64-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 tvOS framework.") + + for app_src in apps: + print(f" Installing app {app_src.name!r}...", end="", flush=True) + app_target = target / f"tvOSTestbed/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 / "tvOSTestbed" / "tvOSTestbed-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 tvOS simulator." + ), + ) + + subcommands = parser.add_subparsers(dest="subcommand") + + clone = subcommands.add_parser( + "clone", + description=( + "Clone the testbed project, copying in an tvOS 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 tvOS process as if they were arguments to " + "`python -m`." + ), + help="Run a testbed project", + ) + run.add_argument( + "--simulator", + default="Apple TV", + help="The name of the simulator to use (default: 'Apple TV')", + ) + 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/tvos-arm64_x86_64-simulator/bin" + ).is_dir(): + print( + f"Testbed does not contain a compiled tvOS 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/tvOS/testbed/app/README b/tvOS/testbed/app/README new file mode 100644 index 00000000000000..af22c685f87976 --- /dev/null +++ b/tvOS/testbed/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/tvOS/testbed/app_packages/README b/tvOS/testbed/app_packages/README new file mode 100644 index 00000000000000..42d7fdeb813250 --- /dev/null +++ b/tvOS/testbed/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/tvOS/testbed/tvOSTestbed.xcodeproj/project.pbxproj b/tvOS/testbed/tvOSTestbed.xcodeproj/project.pbxproj new file mode 100644 index 00000000000000..4e41033183fd83 --- /dev/null +++ b/tvOS/testbed/tvOSTestbed.xcodeproj/project.pbxproj @@ -0,0 +1,514 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + EE7C8A1E2DCD6FF3003206DB /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */; }; + EE7C8A1F2DCD70CD003206DB /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */; }; + EE7C8A202DCD70CD003206DB /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EE7C8A2C2DCD7195003206DB /* app in Resources */ = {isa = PBXBuildFile; fileRef = EE7C8A2B2DCD7195003206DB /* app */; }; + EE7C8A2E2DCD71A6003206DB /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = EE7C8A2D2DCD71A6003206DB /* app_packages */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + EE989E662DCD6E7A0036B268 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = EE989E462DCD6E780036B268 /* Project object */; + proxyType = 1; + remoteGlobalIDString = EE989E4D2DCD6E780036B268; + remoteInfo = tvOSTestbed; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + EE7C8A212DCD70CD003206DB /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + EE7C8A202DCD70CD003206DB /* Python.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; + EE7C8A2B2DCD7195003206DB /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; + EE7C8A2D2DCD71A6003206DB /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; }; + EE989E4E2DCD6E780036B268 /* tvOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; }; + EE989E652DCD6E7A0036B268 /* tvOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = tvOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + EE989E502DCD6E780036B268 /* tvOSTestbed */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = tvOSTestbed; + sourceTree = ""; + }; + EE989E682DCD6E7A0036B268 /* tvOSTestbedTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = tvOSTestbedTests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + EE989E4B2DCD6E780036B268 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + EE7C8A1F2DCD70CD003206DB /* Python.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EE989E622DCD6E7A0036B268 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + EE7C8A1E2DCD6FF3003206DB /* Python.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + EE989E452DCD6E780036B268 = { + isa = PBXGroup; + children = ( + EE7C8A2D2DCD71A6003206DB /* app_packages */, + EE7C8A2B2DCD7195003206DB /* app */, + EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */, + EE989E502DCD6E780036B268 /* tvOSTestbed */, + EE989E682DCD6E7A0036B268 /* tvOSTestbedTests */, + EE989E4F2DCD6E780036B268 /* Products */, + ); + sourceTree = ""; + }; + EE989E4F2DCD6E780036B268 /* Products */ = { + isa = PBXGroup; + children = ( + EE989E4E2DCD6E780036B268 /* tvOSTestbed.app */, + EE989E652DCD6E7A0036B268 /* tvOSTestbedTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + EE989E4D2DCD6E780036B268 /* tvOSTestbed */ = { + isa = PBXNativeTarget; + buildConfigurationList = EE989E792DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "tvOSTestbed" */; + buildPhases = ( + EE989E4A2DCD6E780036B268 /* Sources */, + EE989E4B2DCD6E780036B268 /* Frameworks */, + EE989E4C2DCD6E780036B268 /* Resources */, + EE7C8A222DCD70F4003206DB /* Install Target Specific Python Standard Library */, + EE7C8A232DCD7149003206DB /* Prepare Python Binary Modules */, + EE7C8A212DCD70CD003206DB /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + EE989E502DCD6E780036B268 /* tvOSTestbed */, + ); + name = tvOSTestbed; + packageProductDependencies = ( + ); + productName = tvOSTestbed; + productReference = EE989E4E2DCD6E780036B268 /* tvOSTestbed.app */; + productType = "com.apple.product-type.application"; + }; + EE989E642DCD6E7A0036B268 /* tvOSTestbedTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = EE989E7C2DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "tvOSTestbedTests" */; + buildPhases = ( + EE989E612DCD6E7A0036B268 /* Sources */, + EE989E622DCD6E7A0036B268 /* Frameworks */, + EE989E632DCD6E7A0036B268 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + EE989E672DCD6E7A0036B268 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + EE989E682DCD6E7A0036B268 /* tvOSTestbedTests */, + ); + name = tvOSTestbedTests; + packageProductDependencies = ( + ); + productName = tvOSTestbedTests; + productReference = EE989E652DCD6E7A0036B268 /* tvOSTestbedTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + EE989E462DCD6E780036B268 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1620; + TargetAttributes = { + EE989E4D2DCD6E780036B268 = { + CreatedOnToolsVersion = 16.2; + }; + EE989E642DCD6E7A0036B268 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = EE989E4D2DCD6E780036B268; + }; + }; + }; + buildConfigurationList = EE989E492DCD6E780036B268 /* Build configuration list for PBXProject "tvOSTestbed" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = EE989E452DCD6E780036B268; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = EE989E4F2DCD6E780036B268 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + EE989E4D2DCD6E780036B268 /* tvOSTestbed */, + EE989E642DCD6E7A0036B268 /* tvOSTestbedTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + EE989E4C2DCD6E780036B268 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EE7C8A2E2DCD71A6003206DB /* app_packages in Resources */, + EE7C8A2C2DCD7195003206DB /* app in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EE989E632DCD6E7A0036B268 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + EE7C8A222DCD70F4003206DB /* 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\" = \"-appletvsimulator\" ]; then\n echo \"Installing Python modules for tvOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/tvos-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for tvOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/tvos-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; + }; + EE7C8A232DCD7149003206DB /* 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"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + EE989E4A2DCD6E780036B268 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EE989E612DCD6E7A0036B268 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + EE989E672DCD6E7A0036B268 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = EE989E4D2DCD6E780036B268 /* tvOSTestbed */; + targetProxy = EE989E662DCD6E7A0036B268 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + EE989E772DCD6E7A0036B268 /* 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 = NO; + 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 = NO; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)"; + 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; + HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 18.2; + }; + name = Debug; + }; + EE989E782DCD6E7A0036B268 /* 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 = NO; + 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_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)"; + 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; + HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = appletvos; + TVOS_DEPLOYMENT_TARGET = 18.2; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + EE989E7A2DCD6E7A0036B268 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "tvOSTestbed/tvOSTestbed-Info.plist"; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.python.tvOSTestbed; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Debug; + }; + EE989E7B2DCD6E7A0036B268 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = "tvOSTestbed/tvOSTestbed-Info.plist"; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.python.tvOSTestbed; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + TARGETED_DEVICE_FAMILY = 3; + }; + name = Release; + }; + EE989E7D2DCD6E7A0036B268 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.python.tvOSTestbedTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tvOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tvOSTestbed"; + TVOS_DEPLOYMENT_TARGET = 18.2; + }; + name = Debug; + }; + EE989E7E2DCD6E7A0036B268 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.python.tvOSTestbedTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = 3; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tvOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tvOSTestbed"; + TVOS_DEPLOYMENT_TARGET = 18.2; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + EE989E492DCD6E780036B268 /* Build configuration list for PBXProject "tvOSTestbed" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EE989E772DCD6E7A0036B268 /* Debug */, + EE989E782DCD6E7A0036B268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EE989E792DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "tvOSTestbed" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EE989E7A2DCD6E7A0036B268 /* Debug */, + EE989E7B2DCD6E7A0036B268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EE989E7C2DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "tvOSTestbedTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EE989E7D2DCD6E7A0036B268 /* Debug */, + EE989E7E2DCD6E7A0036B268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = EE989E462DCD6E780036B268 /* Project object */; +} diff --git a/tvOS/testbed/tvOSTestbed/AppDelegate.h b/tvOS/testbed/tvOSTestbed/AppDelegate.h new file mode 100644 index 00000000000000..112c9ed64b83fb --- /dev/null +++ b/tvOS/testbed/tvOSTestbed/AppDelegate.h @@ -0,0 +1,11 @@ +// +// AppDelegate.h +// tvOSTestbed +// + +#import + +@interface AppDelegate : UIResponder + + +@end diff --git a/tvOS/testbed/tvOSTestbed/AppDelegate.m b/tvOS/testbed/tvOSTestbed/AppDelegate.m new file mode 100644 index 00000000000000..bd91fb2d7d6d93 --- /dev/null +++ b/tvOS/testbed/tvOSTestbed/AppDelegate.m @@ -0,0 +1,19 @@ +// +// AppDelegate.m +// tvOSTestbed +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + return YES; +} + +@end diff --git a/tvOS/testbed/tvOSTestbed/Base.lproj/LaunchScreen.storyboard b/tvOS/testbed/tvOSTestbed/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000000000..660ba53de4f73d --- /dev/null +++ b/tvOS/testbed/tvOSTestbed/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tvOS/testbed/tvOSTestbed/dylib-Info-template.plist b/tvOS/testbed/tvOSTestbed/dylib-Info-template.plist new file mode 100644 index 00000000000000..f652e272f71c88 --- /dev/null +++ b/tvOS/testbed/tvOSTestbed/dylib-Info-template.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + + CFBundleIdentifier + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + + iPhoneOS + + MinimumOSVersion + 12.0 + CFBundleVersion + 1 + + diff --git a/tvOS/testbed/tvOSTestbed/main.m b/tvOS/testbed/tvOSTestbed/main.m new file mode 100644 index 00000000000000..d5808fbb9331b7 --- /dev/null +++ b/tvOS/testbed/tvOSTestbed/main.m @@ -0,0 +1,16 @@ +// +// main.m +// tvOSTestbed +// + +#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/tvOS/testbed/tvOSTestbed/tvOSTestbed-Info.plist b/tvOS/testbed/tvOSTestbed/tvOSTestbed-Info.plist new file mode 100644 index 00000000000000..a582f42a212783 --- /dev/null +++ b/tvOS/testbed/tvOSTestbed/tvOSTestbed-Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.python.iOSTestbed + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIRequiresFullScreen + + UILaunchStoryboardName + Launch Screen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + TestArgs + + test + -uall + --single-process + --rerun + -W + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + + + diff --git a/tvOS/testbed/tvOSTestbedTests/tvOSTestbedTests.m b/tvOS/testbed/tvOSTestbedTests/tvOSTestbedTests.m new file mode 100644 index 00000000000000..21d5a2872c1295 --- /dev/null +++ b/tvOS/testbed/tvOSTestbedTests/tvOSTestbedTests.m @@ -0,0 +1,162 @@ +#import +#import + +@interface iOSTestbedTests : XCTestCase + +@end + +@implementation iOSTestbedTests + + +- (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] = "tvOSTestbed"; + 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/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