From 3148e581fb0b4ff18c745c76fd2ab103add0a8d3 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Wed, 4 Jun 2025 02:57:45 +0200 Subject: [PATCH 1/8] [3.14] gh-135101: When choosing the default simulator device, don't use `simctl --set testing` (GH-135102) (#135113) On a fresh Xcode install (including some CI provider configurations), there is no pre-existing testing set that can be used to identify simulator models. Use the default device set to detect available models instead. Live testing simulators are still created in the testing set. (cherry picked from commit dba9de731b231ca0c079205f496d1e3d178b4fd3) Co-authored-by: Joe Rickerby --- iOS/testbed/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 6a5d3e94baf59ee2b73a4017d72e7e86d2fb68d2 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Mon, 3 Feb 2025 09:11:28 +0800 Subject: [PATCH 2/8] Add support for tvOS and watchOS. --- .gitignore | 4 + Lib/platform.py | 75 ++++- Lib/sysconfig/__init__.py | 8 + Misc/platform_triplet.c | 20 ++ configure | 296 ++++++++++++++---- configure.ac | 232 +++++++++++--- iOS/Resources/Info.plist.in | 4 +- tvOS/README.rst | 108 +++++++ tvOS/Resources/Info.plist.in | 34 ++ tvOS/Resources/bin/arm64-apple-tvos-ar | 2 + tvOS/Resources/bin/arm64-apple-tvos-clang | 2 + tvOS/Resources/bin/arm64-apple-tvos-clang++ | 2 + tvOS/Resources/bin/arm64-apple-tvos-cpp | 2 + .../bin/arm64-apple-tvos-simulator-ar | 2 + .../bin/arm64-apple-tvos-simulator-clang | 2 + .../bin/arm64-apple-tvos-simulator-clang++ | 2 + .../bin/arm64-apple-tvos-simulator-cpp | 2 + .../bin/x86_64-apple-tvos-simulator-ar | 2 + .../bin/x86_64-apple-tvos-simulator-clang | 2 + .../bin/x86_64-apple-tvos-simulator-clang++ | 2 + .../bin/x86_64-apple-tvos-simulator-cpp | 2 + tvOS/Resources/dylib-Info-template.plist | 26 ++ tvOS/Resources/pyconfig.h | 7 + watchOS/README.rst | 108 +++++++ watchOS/Resources/Info.plist.in | 34 ++ .../bin/arm64-apple-watchos-simulator-ar | 2 + .../bin/arm64-apple-watchos-simulator-clang | 2 + .../bin/arm64-apple-watchos-simulator-clang++ | 2 + .../bin/arm64-apple-watchos-simulator-cpp | 2 + .../Resources/bin/arm64_32-apple-watchos-ar | 2 + .../bin/arm64_32-apple-watchos-clang | 2 + .../bin/arm64_32-apple-watchos-clang++ | 2 + .../Resources/bin/arm64_32-apple-watchos-cpp | 2 + .../bin/x86_64-apple-watchos-simulator-ar | 2 + .../bin/x86_64-apple-watchos-simulator-clang | 2 + .../x86_64-apple-watchos-simulator-clang++ | 2 + .../bin/x86_64-apple-watchos-simulator-cpp | 2 + watchOS/Resources/dylib-Info-template.plist | 26 ++ watchOS/Resources/pyconfig.h | 11 + 39 files changed, 920 insertions(+), 121 deletions(-) create mode 100644 tvOS/README.rst create mode 100644 tvOS/Resources/Info.plist.in create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-ar create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-clang create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-clang++ create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-cpp create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-simulator-ar create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-simulator-clang create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ create mode 100755 tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp create mode 100755 tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar create mode 100755 tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang create mode 100755 tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ create mode 100755 tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp create mode 100644 tvOS/Resources/dylib-Info-template.plist create mode 100644 tvOS/Resources/pyconfig.h create mode 100644 watchOS/README.rst create mode 100644 watchOS/Resources/Info.plist.in create mode 100755 watchOS/Resources/bin/arm64-apple-watchos-simulator-ar create mode 100755 watchOS/Resources/bin/arm64-apple-watchos-simulator-clang create mode 100755 watchOS/Resources/bin/arm64-apple-watchos-simulator-clang++ create mode 100755 watchOS/Resources/bin/arm64-apple-watchos-simulator-cpp create mode 100755 watchOS/Resources/bin/arm64_32-apple-watchos-ar create mode 100755 watchOS/Resources/bin/arm64_32-apple-watchos-clang create mode 100755 watchOS/Resources/bin/arm64_32-apple-watchos-clang++ create mode 100755 watchOS/Resources/bin/arm64_32-apple-watchos-cpp create mode 100755 watchOS/Resources/bin/x86_64-apple-watchos-simulator-ar create mode 100755 watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang create mode 100755 watchOS/Resources/bin/x86_64-apple-watchos-simulator-clang++ create mode 100755 watchOS/Resources/bin/x86_64-apple-watchos-simulator-cpp create mode 100644 watchOS/Resources/dylib-Info-template.plist create mode 100644 watchOS/Resources/pyconfig.h diff --git a/.gitignore b/.gitignore index 7965f6a404368e..ec0b026b08e7bf 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,10 @@ iOS/testbed/Python.xcframework/ios-*/Python.framework iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace iOS/testbed/iOSTestbed.xcodeproj/xcuserdata iOS/testbed/iOSTestbed.xcodeproj/xcshareddata +tvOS/Frameworks +tvOS/Resources/Info.plist +watchOS/Frameworks +watchOS/Resources/Info.plist Mac/Makefile Mac/PythonLauncher/Info.plist Mac/PythonLauncher/Makefile diff --git a/Lib/platform.py b/Lib/platform.py index 55e211212d4209..5728dfc193562a 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -528,6 +528,54 @@ 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) + + 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 @@ -891,14 +939,25 @@ 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 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 from_subprocess(): """ Fall back to `uname -p` @@ -1058,9 +1117,13 @@ 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() vals = system, node, release, version, machine # Replace 'unknown' values with the more portable '' @@ -1350,6 +1413,10 @@ 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() else: macos_release = mac_ver()[0] if macos_release: diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index f93b98dd681536..1c345039c34376 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -730,6 +730,14 @@ 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 else: import _osx_support osname, release, machine = _osx_support.get_platform_osx( diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index f5cd73bdea8333..50f2f8988b792e 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -257,6 +257,26 @@ 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 // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin diff --git a/configure b/configure index 884f8a4b0680d9..9a4cb5aeaf3b6d 100755 --- a/configure +++ b/configure @@ -982,6 +982,8 @@ LDFLAGS CFLAGS CC HAS_XCRUN +WATCHOS_DEPLOYMENT_TARGET +TVOS_DEPLOYMENT_TARGET IPHONEOS_DEPLOYMENT_TARGET EXPORT_MACOSX_DEPLOYMENT_TARGET CONFIGURE_MACOSX_DEPLOYMENT_TARGET @@ -4116,6 +4118,12 @@ then *-apple-ios*) ac_sys_system=iOS ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-darwin*) ac_sys_system=Darwin ;; @@ -4197,7 +4205,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, 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 +4220,14 @@ 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 ;; *) esac fi @@ -4220,6 +4236,14 @@ 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 ;; *) esac fi @@ -4228,6 +4252,14 @@ 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 ;; *) esac fi @@ -4236,6 +4268,14 @@ 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++ ;; *) esac fi @@ -4358,8 +4398,10 @@ 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\) ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 esac esac @@ -4368,6 +4410,8 @@ 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 ;; *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework @@ -4474,6 +4518,36 @@ 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" + ;; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 @@ -4485,6 +4559,8 @@ 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 ;; *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework @@ -4539,8 +4615,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) + # iOS/tvOS/watchOS 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 +4634,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) + # Always apply the compliance patch on iOS/tvOS/watchOS; 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; } @@ -4614,6 +4690,50 @@ 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 + ;; *-*-darwin*) case "$host_cpu" in arm*) @@ -4704,9 +4824,13 @@ printf "%s\n" "#define _BSD_SOURCE 1" >>confdefs.h define_xopen_source=no;; Darwin/[12][0-9].*) define_xopen_source=no;; - # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. + # On iOS/tvOS/watchOS, defining _POSIX_C_SOURCE also disables platform specific features. iOS/*) define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) + define_xopen_source=no;; # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) @@ -4769,7 +4893,10 @@ 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 enforced by the selected host triple. + + # checks for alternative programs @@ -4810,6 +4937,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 +7316,10 @@ case $ac_sys_system in #( MULTIARCH="" ;; #( iOS) : MULTIARCH="" ;; #( + tvOS) : + MULTIARCH="" ;; #( + watchOS) : + MULTIARCH="" ;; #( FreeBSD*) : MULTIARCH="" ;; #( *) : @@ -7199,7 +7340,7 @@ fi printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( - iOS) : + iOS|tvOS|watchOS) : SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET @@ -7250,6 +7391,14 @@ 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-*-linux-android/clang) : PY_SUPPORT_TIER=3 ;; #( x86_64-*-linux-android/clang) : @@ -7686,7 +7835,7 @@ then case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; - iOS) + iOS|tvOS|watchOS) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; @@ -7752,7 +7901,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) LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) @@ -13574,7 +13723,7 @@ then BLDSHARED="$LDSHARED" fi ;; - iOS/*) + iOS/*|tvOS/*|watchOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" @@ -13707,7 +13856,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/*) LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too @@ -13731,7 +13880,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"; then LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; @@ -15508,7 +15657,7 @@ then : ctypes_malloc_closure=yes ;; #( - iOS) : + iOS|tvOS|watchOS) : ctypes_malloc_closure=yes ;; #( @@ -19260,12 +19409,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 +19469,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 +19895,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 +20213,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 +20481,11 @@ fi fi -# iOS defines some system methods that can be linked (so they are +# iOS/tvOS/watchOS 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" ; then ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" if test "x$ac_cv_func_getentropy" = xyes then : @@ -20400,6 +20507,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 +23998,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 +24113,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 +24296,7 @@ esac fi done +fi # check for long file support functions ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" @@ -24406,10 +24562,10 @@ fi done -# On Android and iOS, clock_settime can be linked (so it is found by +# On Android, iOS, tvOS and watchOS, 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" then for ac_func in clock_settime @@ -26748,8 +26904,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"; then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi @@ -29619,7 +29775,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" ; then ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else @@ -30129,7 +30285,7 @@ else case e in #( with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( - iOS) : + iOS|tvOS|watchOS) : with_ensurepip=no ;; #( *) : with_ensurepip=upgrade @@ -31110,7 +31266,7 @@ case $ac_sys_system in #( ;; #( Darwin) : ;; #( - iOS) : + iOS|tvOS|watchOS) : @@ -35272,6 +35428,8 @@ 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" ;; "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..f904e22e65eb53 100644 --- a/configure.ac +++ b/configure.ac @@ -330,6 +330,12 @@ then *-apple-ios*) ac_sys_system=iOS ;; + *-apple-tvos*) + ac_sys_system=tvOS + ;; + *-apple-watchos*) + ac_sys_system=watchOS + ;; *-*-darwin*) ac_sys_system=Darwin ;; @@ -405,7 +411,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, 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 +426,14 @@ 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 ;; *) esac fi @@ -428,6 +442,14 @@ 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 ;; *) esac fi @@ -436,6 +458,14 @@ 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 ;; *) esac fi @@ -444,6 +474,14 @@ 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++ ;; *) esac fi @@ -558,8 +596,10 @@ 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\) ;; *) AC_MSG_ERROR([Unknown platform for framework build]) esac esac @@ -568,6 +608,8 @@ 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]) ;; *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework @@ -670,6 +712,34 @@ 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]) + ;; *) AC_MSG_ERROR([Unknown platform for framework build]) ;; @@ -678,6 +748,8 @@ 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]) ;; *) PYTHONFRAMEWORK= PYTHONFRAMEWORKDIR=no-framework @@ -730,8 +802,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) + # iOS/tvOS/watchOS 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 +817,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) + # Always apply the compliance patch on iOS/tvOS/watchOS; 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]) ;; @@ -794,6 +866,46 @@ 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 + ;; *-*-darwin*) case "$host_cpu" in arm*) @@ -883,9 +995,13 @@ case $ac_sys_system/$ac_sys_release in define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; - # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. + # On iOS/tvOS/watchOS, defining _POSIX_C_SOURCE also disables platform specific features. iOS/*) define_xopen_source=no;; + tvOS/*) + define_xopen_source=no;; + watchOS/*) + define_xopen_source=no;; # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from # defining NI_NUMERICHOST. QNX/6.3.2) @@ -944,8 +1060,11 @@ 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 enforced by the selected host triple. AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET]) +AC_SUBST([TVOS_DEPLOYMENT_TARGET]) +AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) # checks for alternative programs @@ -979,11 +1098,17 @@ 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 version. 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 +1297,8 @@ AC_MSG_CHECKING([for multiarch]) AS_CASE([$ac_sys_system], [Darwin*], [MULTIARCH=""], [iOS], [MULTIARCH=""], + [tvOS], [MULTIARCH=""], + [watchOS], [MULTIARCH=""], [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) @@ -1193,7 +1320,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], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) @@ -1227,6 +1354,10 @@ 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-*-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 +1667,7 @@ then case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; - iOS) + iOS|tvOS|watchOS) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; @@ -1601,7 +1732,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) LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) @@ -3470,7 +3601,7 @@ then BLDSHARED="$LDSHARED" fi ;; - iOS/*) + iOS/*|tvOS/*|watchOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" @@ -3594,7 +3725,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/*) LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too @@ -3618,7 +3749,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"; then LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' fi ;; @@ -4106,7 +4237,7 @@ AS_VAR_IF([have_libffi], [yes], [ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], - [iOS], [ + [iOS|tvOS|watchOS], [ ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] @@ -5215,9 +5346,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 +5356,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 +5366,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 +5381,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 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" ; 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 +5677,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 +5731,10 @@ AC_CHECK_FUNCS([clock_getres], [], [ ]) ]) -# On Android and iOS, clock_settime can be linked (so it is found by +# On Android, iOS, tvOS and watchOS, 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" then AC_CHECK_FUNCS([clock_settime], [], [ AC_CHECK_LIB([rt], [clock_settime], [ @@ -6345,8 +6485,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"; then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" fi @@ -7005,7 +7145,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" ; then ac_cv_file__dev_ptmx=no ac_cv_file__dev_ptc=no else @@ -7307,7 +7447,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], [with_ensurepip=no], [with_ensurepip=upgrade] ) ]) @@ -7719,7 +7859,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], [ 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/tvOS/README.rst b/tvOS/README.rst new file mode 100644 index 00000000000000..1f793252caf627 --- /dev/null +++ b/tvOS/README.rst @@ -0,0 +1,108 @@ +===================== +Python on tvOS README +===================== + +:Authors: + Russell Keith-Magee (2023-11) + +This document provides a quick overview of some tvOS specific features in the +Python distribution. + +Compilers for building on tvOS +============================== + +Building for tvOS requires the use of Apple's Xcode tooling. It is strongly +recommended that you use the most recent stable release of Xcode, on the +most recently released macOS. + +tvOS specific arguments to configure +=================================== + +* ``--enable-framework[=DIR]`` + + This argument specifies the location where the Python.framework will + be installed. + +* ``--with-framework-name=NAME`` + + Specify the name for the python framework, defaults to ``Python``. + + +Building and using Python on tvOS +================================= + +ABIs and Architectures +---------------------- + +tvOS apps can be deployed on physical devices, and on the tvOS simulator. +Although the API used on these devices is identical, the ABI is different - you +need to link against different libraries for an tvOS device build +(``appletvos``) or an tvOS simulator build (``appletvsimulator``). Apple uses +the XCframework format to allow specifying a single dependency that supports +multiple ABIs. An XCframework is a wrapper around multiple ABI-specific +frameworks. + +tvOS can also support different CPU architectures within each ABI. At present, +there is only a single support ed architecture on physical devices - ARM64. +However, the *simulator* supports 2 architectures - ARM64 (for running on Apple +Silicon machines), and x86_64 (for running on older Intel-based machines.) + +To support multiple CPU architectures on a single platform, Apple uses a "fat +binary" format - a single physical file that contains support for multiple +architectures. + +How do I build Python for tvOS? +------------------------------- + +The Python build system will build a ``Python.framework`` that supports a +*single* ABI with a *single* architecture. If you want to use Python in an tvOS +project, you need to: + +1. Produce multiple ``Python.framework`` builds, one for each ABI and architecture; +2. Merge the binaries for each architecture on a given ABI into a single "fat" binary; +3. Merge the "fat" frameworks for each ABI into a single XCframework. + +tvOS builds of Python *must* be constructed as framework builds. To support this, +you must provide the ``--enable-framework`` flag when configuring the build. + +The build also requires the use of cross-compilation. The commands for building +Python for tvOS will look somethign like:: + + $ ./configure \ + --enable-framework=/path/to/install \ + --host=aarch64-apple-tvos \ + --build=aarch64-apple-darwin \ + --with-build-python=/path/to/python.exe + $ make + $ make install + +In this invocation: + +* ``/path/to/install`` is the location where the final Python.framework will be + output. + +* ``--host`` is the architecture and ABI that you want to build, in GNU compiler + triple format. This will be one of: + + - ``aarch64-apple-tvos`` for ARM64 tvOS devices. + - ``aarch64-apple-tvos-simulator`` for the tvOS simulator running on Apple + Silicon devices. + - ``x86_64-apple-tvos-simulator`` for the tvOS simulator running on Intel + devices. + +* ``--build`` is the GNU compiler triple for the machine that will be running + the compiler. This is one of: + + - ``aarch64-apple-darwin`` for Apple Silicon devices. + - ``x86_64-apple-darwin`` for Intel devices. + +* ``/path/to/python.exe`` is the path to a Python binary on the machine that + will be running the compiler. This is needed because the Python compilation + process involves running some Python code. On a normal desktop build of + Python, you can compile a python interpreter and then use that interpreter to + run Python code. However, the binaries produced for tvOS won't run on macOS, so + you need to provide an external Python interpreter. This interpreter must be + the version as the Python that is being compiled. + +Using a framework-based Python on tvOS +====================================== diff --git a/tvOS/Resources/Info.plist.in b/tvOS/Resources/Info.plist.in new file mode 100644 index 00000000000000..ab3050804b8c58 --- /dev/null +++ b/tvOS/Resources/Info.plist.in @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Python + CFBundleGetInfoString + Python Runtime and Library + CFBundleIdentifier + @PYTHONFRAMEWORKIDENTIFIER@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Python + CFBundlePackageType + FMWK + CFBundleShortVersionString + %VERSION% + CFBundleLongVersionString + %VERSION%, (c) 2001-2024 Python Software Foundation. + CFBundleSignature + ???? + CFBundleVersion + 1 + CFBundleSupportedPlatforms + + tvOS + + MinimumOSVersion + @TVOS_DEPLOYMENT_TARGET@ + + diff --git a/tvOS/Resources/bin/arm64-apple-tvos-ar b/tvOS/Resources/bin/arm64-apple-tvos-ar new file mode 100755 index 00000000000000..e302748a13c8b1 --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-ar @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvos${TVOS_SDK_VERSION} ar "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-clang b/tvOS/Resources/bin/arm64-apple-tvos-clang new file mode 100755 index 00000000000000..bef66ed852e1dd --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-clang @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-clang++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++ new file mode 100755 index 00000000000000..04ca4df9ff00e5 --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++ @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-cpp b/tvOS/Resources/bin/arm64-apple-tvos-cpp new file mode 100755 index 00000000000000..cb797b5a5308cd --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos -E "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar new file mode 100755 index 00000000000000..87ef5015aae9b4 --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang new file mode 100755 index 00000000000000..729f3756fbcab2 --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ new file mode 100755 index 00000000000000..f98b36a6c8f9b6 --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos-simulator "$@" diff --git a/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp new file mode 100755 index 00000000000000..40555262ec280e --- /dev/null +++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator -E "$@" diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar new file mode 100755 index 00000000000000..87ef5015aae9b4 --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang new file mode 100755 index 00000000000000..27b93b55bd8b6d --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator "$@" diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ new file mode 100755 index 00000000000000..df083314351e96 --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target x86_64-apple-tvos-simulator "$@" diff --git a/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp new file mode 100755 index 00000000000000..ad0c98ac52c312 --- /dev/null +++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp @@ -0,0 +1,2 @@ +#!/bin/bash +xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator -E "$@" diff --git a/tvOS/Resources/dylib-Info-template.plist b/tvOS/Resources/dylib-Info-template.plist new file mode 100644 index 00000000000000..a20d476fa7b552 --- /dev/null +++ b/tvOS/Resources/dylib-Info-template.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + + CFBundleIdentifier + + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSupportedPlatforms + + tvOS + + MinimumOSVersion + 9.0 + CFBundleVersion + 1 + + diff --git a/tvOS/Resources/pyconfig.h b/tvOS/Resources/pyconfig.h new file mode 100644 index 00000000000000..4acff2c6051637 --- /dev/null +++ b/tvOS/Resources/pyconfig.h @@ -0,0 +1,7 @@ +#ifdef __arm64__ +#include "pyconfig-arm64.h" +#endif + +#ifdef __x86_64__ +#include "pyconfig-x86_64.h" +#endif diff --git a/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 From 68b83a6ed8cff2740610c415c1a15bd90677a709 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 23 Apr 2025 14:33:51 +0800 Subject: [PATCH 3/8] Add support for visionOS. --- .gitignore | 10 + Lib/ctypes/__init__.py | 2 +- Lib/ctypes/util.py | 4 +- Lib/importlib/_bootstrap_external.py | 4 +- Lib/platform.py | 37 +- Lib/site.py | 4 +- Lib/subprocess.py | 2 +- Lib/sysconfig/__init__.py | 9 +- Lib/test/datetimetester.py | 4 +- Lib/test/support/__init__.py | 4 +- Lib/test/test_ctypes/test_dllist.py | 2 +- Lib/test/test_platform.py | 14 +- Lib/test/test_webbrowser.py | 3 +- Lib/webbrowser.py | 8 +- Makefile.pre.in | 44 +- Misc/platform_triplet.c | 6 + config.sub | 4 +- configure | 125 +++- configure.ac | 118 +++- visionOS/Resources/Info.plist.in | 34 + visionOS/Resources/bin/arm64-apple-xros-ar | 2 + visionOS/Resources/bin/arm64-apple-xros-clang | 2 + .../Resources/bin/arm64-apple-xros-clang++ | 2 + visionOS/Resources/bin/arm64-apple-xros-cpp | 2 + .../bin/arm64-apple-xros-simulator-ar | 2 + .../bin/arm64-apple-xros-simulator-clang | 2 + .../bin/arm64-apple-xros-simulator-clang++ | 2 + .../bin/arm64-apple-xros-simulator-cpp | 2 + visionOS/Resources/dylib-Info-template.plist | 30 + .../testbed/Python.xcframework/Info.plist | 43 ++ .../xros-arm64-simulator/README | 4 + .../Python.xcframework/xros-arm64/README | 4 + visionOS/testbed/__main__.py | 512 +++++++++++++++ .../visionOSTestbed.xcodeproj/project.pbxproj | 581 ++++++++++++++++++ .../testbed/visionOSTestbed/AppDelegate.h | 11 + .../testbed/visionOSTestbed/AppDelegate.m | 19 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Assets.xcassets/Contents.json | 6 + visionOS/testbed/visionOSTestbed/app/README | 7 + .../visionOSTestbed/app_packages/README | 7 + .../visionOSTestbed/dylib-Info-template.plist | 30 + visionOS/testbed/visionOSTestbed/main.m | 16 + .../visionOSTestbed-Info.plist | 56 ++ .../visionOSTestbedTests.m | 162 +++++ 45 files changed, 1888 insertions(+), 78 deletions(-) create mode 100644 visionOS/Resources/Info.plist.in create mode 100755 visionOS/Resources/bin/arm64-apple-xros-ar create mode 100755 visionOS/Resources/bin/arm64-apple-xros-clang create mode 100755 visionOS/Resources/bin/arm64-apple-xros-clang++ create mode 100755 visionOS/Resources/bin/arm64-apple-xros-cpp create mode 100755 visionOS/Resources/bin/arm64-apple-xros-simulator-ar create mode 100755 visionOS/Resources/bin/arm64-apple-xros-simulator-clang create mode 100755 visionOS/Resources/bin/arm64-apple-xros-simulator-clang++ create mode 100755 visionOS/Resources/bin/arm64-apple-xros-simulator-cpp create mode 100644 visionOS/Resources/dylib-Info-template.plist create mode 100644 visionOS/testbed/Python.xcframework/Info.plist create mode 100644 visionOS/testbed/Python.xcframework/xros-arm64-simulator/README create mode 100644 visionOS/testbed/Python.xcframework/xros-arm64/README create mode 100644 visionOS/testbed/__main__.py create mode 100644 visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj create mode 100644 visionOS/testbed/visionOSTestbed/AppDelegate.h create mode 100644 visionOS/testbed/visionOSTestbed/AppDelegate.m create mode 100644 visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json create mode 100644 visionOS/testbed/visionOSTestbed/app/README create mode 100644 visionOS/testbed/visionOSTestbed/app_packages/README create mode 100644 visionOS/testbed/visionOSTestbed/dylib-Info-template.plist create mode 100644 visionOS/testbed/visionOSTestbed/main.m create mode 100644 visionOS/testbed/visionOSTestbed/visionOSTestbed-Info.plist create mode 100644 visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m diff --git a/.gitignore b/.gitignore index ec0b026b08e7bf..3893a91e77198a 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ Lib/test/data/* /Makefile /Makefile.pre /iOSTestbed.* +/visionOSTestbed.* iOS/Frameworks/ iOS/Resources/Info.plist iOS/testbed/build @@ -81,10 +82,19 @@ iOS/testbed/Python.xcframework/ios-*/Python.framework iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace iOS/testbed/iOSTestbed.xcodeproj/xcuserdata iOS/testbed/iOSTestbed.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 5728dfc193562a..cad919bc0c4969 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -576,6 +576,30 @@ def watchos_ver(system="", release="", model="", is_simulator=False): 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 @@ -775,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 @@ -939,7 +963,7 @@ def get_OpenVMS(): csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' - # On the iOS/tvOS/watchOS simulator, os.uname returns the architecture as + # 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. @@ -958,6 +982,11 @@ def get_watchos(): 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` @@ -1124,6 +1153,8 @@ def uname(): 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 '' @@ -1417,6 +1448,8 @@ def platform(aliased=False, terse=False): 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 1c345039c34376..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): @@ -738,6 +741,10 @@ def get_platform(): 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..e436b2efb8e205 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,41 @@ 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)" run --verbose -- test -uall --single-process --rerun -W + $(PYTHON_FOR_BUILD) "$(XCFOLDER-visionOS)" run --verbose -- test -uall --single-process --rerun -W # Like test, but using --slow-ci which enables all test resources and use # longer timeout. Run an optional pybuildbot.identify script to include diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index 50f2f8988b792e..6c1863c943b353 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -277,6 +277,12 @@ PLATFORM_TRIPLET=arm64-watchsimulator # 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 9a4cb5aeaf3b6d..7c93d36e71729b 100755 --- a/configure +++ b/configure @@ -982,6 +982,8 @@ LDFLAGS CFLAGS CC HAS_XCRUN +EXPORT_XROS_DEPLOYMENT_TARGET +XROS_DEPLOYMENT_TARGET WATCHOS_DEPLOYMENT_TARGET TVOS_DEPLOYMENT_TARGET IPHONEOS_DEPLOYMENT_TARGET @@ -4124,6 +4126,9 @@ then *-apple-watchos*) ac_sys_system=watchOS ;; + *-apple-xros*) + ac_sys_system=visionOS + ;; *-*-darwin*) ac_sys_system=Darwin ;; @@ -4205,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/tvOS/watchOS, 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 @@ -4228,6 +4233,9 @@ if test -z "$AR"; then 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 @@ -4244,6 +4252,9 @@ if test -z "$CC"; then 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 @@ -4260,6 +4271,9 @@ if test -z "$CPP"; then 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 @@ -4276,6 +4290,9 @@ if test -z "$CXX"; then 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 @@ -4402,6 +4419,7 @@ then : 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 @@ -4412,6 +4430,7 @@ then : 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 @@ -4548,6 +4567,21 @@ then : 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 @@ -4561,6 +4595,7 @@ else case e 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 @@ -4615,8 +4650,8 @@ then : case "$withval" in yes) case $ac_sys_system in - Darwin|iOS|tvOS|watchOS) - # iOS/tvOS/watchOS 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 ;; @@ -4634,8 +4669,8 @@ printf "%s\n" "applying custom app store compliance patch" >&6; } else case e in #( e) case $ac_sys_system in - iOS|tvOS|watchOS) - # Always apply the compliance patch on iOS/tvOS/watchOS; 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; } @@ -4653,6 +4688,8 @@ fi +EXPORT_XROS_DEPLOYMENT_TARGET='#' + if test "$cross_compiling" = yes; then case "$host" in @@ -4734,6 +4771,34 @@ printf "%s\n" "$WATCHOS_DEPLOYMENT_TARGET" >&6; } ;; 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*) @@ -4824,13 +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/tvOS/watchOS, 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) @@ -4894,10 +4961,13 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' # Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / -# WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. +# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. + +# XROS_DEPLOYMENT_TARGET should get exported + # checks for alternative programs @@ -7320,6 +7390,8 @@ case $ac_sys_system in #( MULTIARCH="" ;; #( watchOS) : MULTIARCH="" ;; #( + visionOS) : + MULTIARCH="" ;; #( FreeBSD*) : MULTIARCH="" ;; #( *) : @@ -7340,7 +7412,7 @@ fi printf "%s\n" "$MULTIARCH" >&6; } case $ac_sys_system in #( - iOS|tvOS|watchOS) : + iOS|tvOS|watchOS|visionOS) : SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( *) : SOABI_PLATFORM=$PLATFORM_TRIPLET @@ -7399,6 +7471,10 @@ case $host/$ac_cv_cc_name in #( 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) : @@ -7835,7 +7911,7 @@ then case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; - iOS|tvOS|watchOS) + iOS|tvOS|watchOS|visionOS) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; @@ -7901,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|tvOS|watchOS) + iOS|tvOS|watchOS|visionOS) LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) @@ -13723,7 +13799,7 @@ then BLDSHARED="$LDSHARED" fi ;; - iOS/*|tvOS/*|watchOS/*) + iOS/*|tvOS/*|watchOS/*|visionOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" @@ -13856,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/*|tvOS/*|watchOS/*) + Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too @@ -13880,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" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS"; 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 ;; @@ -15657,7 +15733,7 @@ then : ctypes_malloc_closure=yes ;; #( - iOS|tvOS|watchOS) : + iOS|tvOS|watchOS|visionOS) : ctypes_malloc_closure=yes ;; #( @@ -20481,11 +20557,11 @@ fi fi -# iOS/tvOS/watchOS define 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" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; 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 : @@ -24562,10 +24638,10 @@ fi done -# On Android, iOS, tvOS and watchOS, 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" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" +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 @@ -24882,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" @@ -26905,7 +26981,7 @@ if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MA fi # 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"; then +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 @@ -29775,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" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" ; 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 @@ -30285,7 +30361,7 @@ else case e in #( with_ensurepip=no ;; #( WASI) : with_ensurepip=no ;; #( - iOS|tvOS|watchOS) : + iOS|tvOS|watchOS|visionOS) : with_ensurepip=no ;; #( *) : with_ensurepip=upgrade @@ -31234,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=;; @@ -31266,7 +31342,7 @@ case $ac_sys_system in #( ;; #( Darwin) : ;; #( - iOS|tvOS|watchOS) : + iOS|tvOS|watchOS|visionOS) : @@ -35430,6 +35506,7 @@ do "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 f904e22e65eb53..7ab0609bf8ab3e 100644 --- a/configure.ac +++ b/configure.ac @@ -336,6 +336,9 @@ then *-apple-watchos*) ac_sys_system=watchOS ;; + *-apple-xros*) + ac_sys_system=visionOS + ;; *-*-darwin*) ac_sys_system=Darwin ;; @@ -411,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/tvOS/watchOS, 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 @@ -434,6 +437,9 @@ if test -z "$AR"; then 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 @@ -450,6 +456,9 @@ if test -z "$CC"; then 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 @@ -466,6 +475,9 @@ if test -z "$CPP"; then 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 @@ -482,6 +494,9 @@ if test -z "$CXX"; then 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 @@ -600,6 +615,7 @@ AC_ARG_ENABLE([framework], 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 @@ -610,6 +626,7 @@ AC_ARG_ENABLE([framework], 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 @@ -740,6 +757,20 @@ AC_ARG_ENABLE([framework], 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]) ;; @@ -750,6 +781,7 @@ AC_ARG_ENABLE([framework], 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 @@ -802,8 +834,8 @@ AC_ARG_WITH( case "$withval" in yes) case $ac_sys_system in - Darwin|iOS|tvOS|watchOS) - # iOS/tvOS/watchOS 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]) ;; @@ -817,8 +849,8 @@ AC_ARG_WITH( esac ],[ case $ac_sys_system in - iOS|tvOS|watchOS) - # Always apply the compliance patch on iOS/tvOS/watchOS; 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]) ;; @@ -831,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 @@ -906,6 +940,30 @@ if test "$cross_compiling" = yes; then ;; 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*) @@ -995,13 +1053,15 @@ case $ac_sys_system/$ac_sys_release in define_xopen_source=no;; Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) define_xopen_source=no;; - # On iOS/tvOS/watchOS, 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) @@ -1061,10 +1121,13 @@ CONFIGURE_MACOSX_DEPLOYMENT_TARGET= EXPORT_MACOSX_DEPLOYMENT_TARGET='#' # Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / -# WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. +# 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 @@ -1098,7 +1161,9 @@ AS_CASE([$host], ], ) -dnl Add the compiler flag for the iOS/tvOS/watchOS 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}"]) @@ -1299,6 +1364,7 @@ AS_CASE([$ac_sys_system], [iOS], [MULTIARCH=""], [tvOS], [MULTIARCH=""], [watchOS], [MULTIARCH=""], + [visionOS], [MULTIARCH=""], [FreeBSD*], [MULTIARCH=""], [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] ) @@ -1320,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|tvOS|watchOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], + [iOS|tvOS|watchOS|visionOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], [SOABI_PLATFORM=$PLATFORM_TRIPLET] ) @@ -1358,6 +1424,8 @@ AS_CASE([$host/$ac_cv_cc_name], [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 @@ -1667,7 +1735,7 @@ then case $ac_sys_system in Darwin) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; - iOS|tvOS|watchOS) + iOS|tvOS|watchOS|visionOS) LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; *) AC_MSG_ERROR([Unknown platform for framework build]);; @@ -1732,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|tvOS|watchOS) + iOS|tvOS|watchOS|visionOS) LDLIBRARY='libpython$(LDVERSION).dylib' ;; AIX*) @@ -3601,7 +3669,7 @@ then BLDSHARED="$LDSHARED" fi ;; - iOS/*|tvOS/*|watchOS/*) + iOS/*|tvOS/*|watchOS/*|visionOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' BLDSHARED="$LDSHARED" @@ -3725,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/*|tvOS/*|watchOS/*) + Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) LINKFORSHARED="$extra_undefs -framework CoreFoundation" # Issue #18075: the default maximum stack size (8MBytes) is too @@ -3749,7 +3817,7 @@ then LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' fi LINKFORSHARED="$LINKFORSHARED" - elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS"; 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 ;; @@ -4237,7 +4305,7 @@ AS_VAR_IF([have_libffi], [yes], [ dnl when do we need USING_APPLE_OS_LIBFFI? ctypes_malloc_closure=yes ], - [iOS|tvOS|watchOS], [ + [iOS|tvOS|watchOS|visionOS], [ ctypes_malloc_closure=yes ], [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] @@ -5381,11 +5449,11 @@ if test "$MACHDEP" != linux; then AC_CHECK_FUNCS([lchmod]) fi -# iOS/tvOS/watchOS define 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" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then +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 @@ -5731,10 +5799,10 @@ AC_CHECK_FUNCS([clock_getres], [], [ ]) ]) -# On Android, iOS, tvOS and watchOS, 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" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" +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], [ @@ -5892,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" @@ -6486,7 +6554,7 @@ if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MA fi # 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"; then +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 @@ -7145,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" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" ; 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 @@ -7447,7 +7515,7 @@ AC_ARG_WITH([ensurepip], AS_CASE([$ac_sys_system], [Emscripten], [with_ensurepip=no], [WASI], [with_ensurepip=no], - [iOS|tvOS|watchOS], [with_ensurepip=no], + [iOS|tvOS|watchOS|visionOS], [with_ensurepip=no], [with_ensurepip=upgrade] ) ]) @@ -7834,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=;; @@ -7859,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|tvOS|watchOS], [ + [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/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 From 5aef001dcc4f09d4d41209eeeacaba7c5f300d1c Mon Sep 17 00:00:00 2001 From: John Zhou Date: Tue, 3 Jun 2025 16:02:15 -0500 Subject: [PATCH 4/8] Squashed commit of the following: commit a2288879b4c6748dd03beace85d7ce3a1a4a7c61 Author: John Zhou Date: Sat May 17 19:53:30 2025 -0500 https://en.wikipedia.org/wiki/Memory_leak#Pseudocode commit 78eb7040a4a80307b803ebbbb6d2f2889a75318e Author: John Zhou Date: Thu May 15 22:12:29 2025 -0500 Revert "try to build multiprocessing" This reverts commit 4e92456c63db3bd0e001c2ebd5f51fcbd74e4caf. commit 687916e32d0f1543fba18795b2432e40dff550da Author: John Zhou Date: Thu May 15 22:07:48 2025 -0500 try to build multiprocessing commit 02be1ef1b86da781c631d1cc96940c66121f38e9 Author: John Zhou Date: Thu May 15 18:31:47 2025 -0500 misc fixes commit 1ff1574c105a3f62f864d01982d37c1b287a50bd Author: John Zhou Date: Thu May 15 17:14:16 2025 -0500 why do i forget to handle exceptions??? commit ff71f936dbb4a535c7184ac8c9705fc2225d88b7 Author: John Zhou Date: Thu May 15 16:54:10 2025 -0500 grumble commit 8d3f831c74aef8bc31676c8afaaa7b3e125a9279 Author: John Zhou Date: Wed May 14 20:23:37 2025 -0500 grumble commit 5d73cfcea2f1d93c216acfe4a28e1c85d3fd0b9d Author: John Zhou Date: Wed May 14 20:17:51 2025 -0500 fix test_os commit 084a83cda34b81cf8171cecef7170e3fb00f02f1 Author: John Zhou Date: Sat May 10 21:50:13 2025 -0500 disable sandbox commit c8227e453c5e837726b25afa6d992a55393108ee Author: John Zhou Date: Thu May 8 22:07:42 2025 -0500 platform fix commit 00c08c3e3b4fd7f1d9accc29393a1e56c4132128 Author: John Zhou Date: Thu May 8 21:28:54 2025 -0500 fix platform on tvOS et. al. commit 63433a61cdd833aff77ea1de80ebbdb78a616d93 Author: John Zhou Date: Mon May 5 20:57:35 2025 -0500 add a missing file commit 99308362797f12e94569c6a34d3a5be1e2cffc2d Author: John Zhou Date: Sat May 10 07:42:54 2025 -0500 use python for build commit 1ee48cdb4cd04a06adae7ad37dfe74e045e5f326 Author: John Zhou Date: Sat May 3 19:23:52 2025 -0500 install binaries commit 19e8d645a3925be706e31e3a169ad68252536d55 Author: John Zhou Date: Sat May 3 14:39:59 2025 -0500 merge test commands commit 639bf33dd83d753cc94978487190ac82622f832d Author: John Zhou Date: Sat May 3 09:02:19 2025 -0500 elif commit f1b9686a4aa472fd79dd0839a46f71722f0f3410 Author: John Zhou Date: Sat May 3 09:00:09 2025 -0500 another fix commit 7253cddde910965a5f334b20351e01a6ef537216 Author: John Zhou Date: Sat May 3 08:53:54 2025 -0500 fix test commit ab9480ea2262eaaeec1f05d6efa60797c824e86f Author: John Zhou Date: Fri May 2 20:54:59 2025 -0500 grumble commit 5ec256452d3859a5978ba1bea19ec4fc5ea0e4da Author: John Zhou Date: Fri May 2 20:50:18 2025 -0500 nvm that did not work commit 59e62e605b82027af7c13833bec30a1c0907b3ac Author: John Zhou Date: Fri May 2 20:34:26 2025 -0500 more fixes, also parallel commit da68fb674b448c5853db625c57c878bbeff04255 Author: John Zhou Date: Fri May 2 18:49:29 2025 -0500 remove irrel comment commit d6620987cb46e7619a1714bb41f02d1bc951b1cb Author: John Date: Fri May 2 18:44:00 2025 -0500 Update test_util.py commit 06e851050a7ecf00cdbe43ed979c8b720396bb77 Author: John Date: Fri May 2 18:43:24 2025 -0500 Update test_loader.py commit daf3275f6ebef8a4b751adcc892ab1bd1f3a8e4c Author: John Date: Fri May 2 18:42:41 2025 -0500 Update __init__.py commit aaf27df0c608a06edb8d2f25c933b3440fd63f28 Author: John Date: Fri May 2 18:41:56 2025 -0500 Update test_misc.py commit 158827523ec7f9a1cdd3677f3f854f9b1e788a2d Author: John Date: Fri May 2 18:41:24 2025 -0500 Update datetimetester.py commit fc008b8108591e9aa021cd7916653da2d6786315 Author: John Zhou Date: Fri May 2 18:44:37 2025 -0500 stuff commit da006ccc6aa85963e2a3f599a3d587955495a588 Author: John Zhou Date: Fri May 2 18:40:58 2025 -0500 add flag for fwork commit ad7a0e1cc8c1da782e07a8f43efb6cc4d2c3ba78 Author: John Zhou Date: Fri May 2 18:34:53 2025 -0500 haaaands commit a29e718c76fc8494b31e2649cdb382404b9d9dbe Author: John Zhou Date: Fri May 2 18:26:38 2025 -0500 yet another error commit 8b41226b1080de9a5058040d4ae739429bb98af9 Author: John Zhou Date: Fri May 2 18:08:23 2025 -0500 outdated message commit 6c07c0075eedd4803d23c154af03fbc4acfce23c Author: John Zhou Date: Thu May 1 21:49:34 2025 -0500 more fixups commit 897e182ae4793aacb52c6f1d7225313ecc19f6fb Author: John Zhou Date: Thu May 1 20:58:06 2025 -0500 another ref commit 5edde3aaafdeedee484f08075e8e242bb0f9100c Author: John Zhou Date: Tue Apr 29 20:12:19 2025 -0500 whitespace commit 3ece1d6d49d1a727186ab0ae7f42e747b2f65136 Author: John Zhou Date: Tue Apr 29 18:11:42 2025 -0500 I'm not sure if multiprocessing is available... commit c47a04b6212ed04092ddfdca608b13a48becfa3c Author: John Zhou Date: Tue Apr 29 17:39:16 2025 -0500 enable testing stuff commit fecd3b5860ed4fcc84ff0267e925ed23dcf95679 Author: John Zhou Date: Tue Apr 29 17:23:02 2025 -0500 support fork, fix another appleframeworkloader reference commit 834b2075fc529e731f093e1e641dacb2f1647552 Author: John Zhou Date: Tue Apr 29 16:52:59 2025 -0500 detect mac catalyst in datetimetester commit 03a40b1e3f9781b9da7a73bd01dbf9e092c47844 Author: John Zhou Date: Tue Apr 29 07:47:53 2025 -0500 use sys commit 849e276eab43d61fe4e0d48913ad0ec01e781965 Author: John Zhou Date: Tue Apr 29 07:28:55 2025 -0500 bootstrap external cleanup` commit 65e3bf81499f515c77e8074382a13b68d99f65c9 Author: John Zhou Date: Tue Apr 29 07:20:31 2025 -0500 (untested) add mac catalyst detection commit 8e008c4829ad99745643c9df7172ecf2303a7d36 Author: John Zhou Date: Mon Apr 28 21:37:37 2025 -0500 minor adjustments to test script commit 3d539a493622d2058d9602208744aefe0ffca12c Author: John Zhou Date: Mon Apr 28 21:30:45 2025 -0500 disable lib valid + use ad hoc commit d16af173652184025586f7bf18aecf9c276a9c1f Author: John Zhou Date: Sun Apr 27 21:49:08 2025 -0500 change the regex for catalyst and add if commit 823d7b6e37a40d8dfcf3e86c9c2d5d33ca734212 Author: John Zhou Date: Sun Apr 27 21:43:57 2025 -0500 add missing entitlemnet commit 5604df876f19d913402898e49b374b66afc422de Author: John Zhou Date: Sun Apr 27 21:32:21 2025 -0500 whitespace commit 5f767fdf71e4aafd8da68baa3790639a5f451552 Author: John Zhou Date: Tue Jun 3 08:12:12 2025 -0500 glue for testbed commit e7b83aea0830ed2f1cca1afc40c9e67a87f797bb Author: John Zhou Date: Sun Apr 27 18:11:17 2025 -0500 fix plist commit de0ab4d2e88f3f3838b9cd2f075031932730c281 Author: John Zhou Date: Sun Apr 27 18:09:32 2025 -0500 address review commit 87482cdae52797e298ec610047fa514db053f084 Author: John Date: Sun Apr 27 14:53:52 2025 -0500 Delete MacCatalyst/Resources/pyconfig.h commit 2e971be573955bb252975d28bcd37eba4d3dceb8 Author: John Zhou Date: Sun Apr 27 12:27:10 2025 -0500 plist adjustments + kill maccatalyst testbed revert changes commit 8d186e3dc3b6a8845d344da69f2c282cd37c953e Author: John Zhou Date: Sun Apr 27 11:40:04 2025 -0500 esac commit 1a20cd79b1a40d938116b7e9276d2c6aaba7e94c Author: John Zhou Date: Sun Apr 27 11:38:09 2025 -0500 fixes for plist commit f00cadff41d8a3c3aafc2919f134787af27fc7e9 Author: John Zhou Date: Sun Apr 27 10:47:38 2025 -0500 git rebase cleanup commit 956cfbd78b77f69342472c69543e4da8969d92c6 Author: John Zhou Date: Sat Apr 26 07:00:24 2025 -0500 Remove ignored files from tracking commit 3828ce41be9432750c99da6ad6f4aa64a9c216eb Author: John Zhou Date: Sat Apr 26 07:00:09 2025 -0500 remove more ios refs commit bd99ef59d41026f314e6f8fa74d618997ea7ecd6 Author: John Zhou Date: Sat Apr 26 06:49:55 2025 -0500 testbed commit 2479a3524e2faa5d195e3b23d8e797150b6971aa Author: John Zhou Date: Sat Apr 26 06:11:51 2025 -0500 here more code commit b3adac46b6a8e45ce170af20574f54f9627c41ba Author: John Zhou Date: Sat Apr 26 05:50:09 2025 -0500 fix commit ce636cd9dd56eeee4fb37bf3f5806189e205b75c Author: John Zhou Date: Sat Apr 26 05:47:45 2025 -0500 rpath fix w/ embedded commit e0ec373390f748fe71d61440b95341e094ddc42f Author: John Zhou Date: Sun Apr 27 10:46:32 2025 -0500 Minor cleanups. commit 4071f1d51ae022671da15a05a79112edae086bbd Author: John Zhou Date: Fri Apr 25 20:36:44 2025 -0500 remove useless stuff. commit 54856e3b93fc8a5dab1d2d04b69e3e58323140b8 Author: John Zhou Date: Fri Apr 25 18:48:41 2025 -0500 another fix commit 97598c4e8a3f22637e22a7bf3e838f2152ce2eea Author: John Zhou Date: Fri Apr 25 18:43:42 2025 -0500 another fix commit f39ed640c749ccaf6be1c07631e93caa263bae4e Author: John Zhou Date: Fri Apr 25 18:38:12 2025 -0500 more changes to configure commit 9de10c04933acbb6427f77969b169c43c68ffcb5 Author: John Zhou Date: Fri Apr 25 18:30:44 2025 -0500 Add supp for mac catalyst Co-Authored-By: Andrew Savva --- .gitignore | 1 + Lib/_ios_support.py | 3 +- Lib/importlib/_bootstrap_external.py | 3 +- Lib/platform.py | 15 +- Lib/subprocess.py | 5 +- Lib/test/datetimetester.py | 6 +- Lib/test/support/__init__.py | 9 + Lib/test/test_capi/test_misc.py | 4 +- Lib/test/test_import/__init__.py | 10 +- .../test_importlib/extension/test_finder.py | 4 +- .../test_importlib/extension/test_loader.py | 8 +- Lib/test/test_importlib/test_util.py | 2 +- Lib/test/test_importlib/util.py | 4 +- Lib/test/test_os.py | 58 +++++-- Lib/test/test_platform.py | 13 +- MacCatalyst/Resources/Info.plist.in | 38 +++++ .../Resources/bin/arm64-apple-ios-macabi-ar | 2 + .../bin/arm64-apple-ios-macabi-clang | 2 + .../bin/arm64-apple-ios-macabi-clang++ | 2 + .../Resources/bin/arm64-apple-ios-macabi-cpp | 2 + .../Resources/bin/x86_64-apple-ios-macabi-ar | 2 + .../bin/x86_64-apple-ios-macabi-clang | 2 + .../bin/x86_64-apple-ios-macabi-clang++ | 2 + .../Resources/bin/x86_64-apple-ios-macabi-cpp | 2 + MacCatalyst/Resources/pyconfig.h | 7 + Makefile.pre.in | 31 ++-- Misc/platform_triplet.c | 6 + config.sub | 4 +- configure | 136 +++++++++++++-- configure.ac | 118 +++++++++++-- iOS/testbed/Python.xcframework/Info.plist | 17 ++ .../ios-arm64_x86_64-maccatalyst/README | 4 + iOS/testbed/__main__.py | 156 ++++++++++++------ .../iOSTestbed.xcodeproj/project.pbxproj | 28 +++- .../iOSTestbed/iOSTestbed.entitlements | 8 + 35 files changed, 573 insertions(+), 141 deletions(-) create mode 100644 MacCatalyst/Resources/Info.plist.in create mode 100755 MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar create mode 100755 MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang create mode 100755 MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ create mode 100755 MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp create mode 100755 MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar create mode 100755 MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang create mode 100755 MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ create mode 100755 MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp create mode 100644 MacCatalyst/Resources/pyconfig.h create mode 100644 iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README create mode 100644 iOS/testbed/iOSTestbed/iOSTestbed.entitlements diff --git a/.gitignore b/.gitignore index 3893a91e77198a..4c5221f4f62f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ iOS/testbed/Python.xcframework/ios-*/Python.framework iOS/testbed/iOSTestbed.xcodeproj/project.xcworkspace iOS/testbed/iOSTestbed.xcodeproj/xcuserdata iOS/testbed/iOSTestbed.xcodeproj/xcshareddata +MacCatalyst/Resources/Info.plist visionOS/testbed/Python.xcframework/xros-*/bin visionOS/testbed/Python.xcframework/xros-*/include visionOS/testbed/Python.xcframework/xros-*/lib diff --git a/Lib/_ios_support.py b/Lib/_ios_support.py index 20467a7c2bcaeb..deefacaf8c5234 100644 --- a/Lib/_ios_support.py +++ b/Lib/_ios_support.py @@ -25,6 +25,7 @@ def get_platform_ios(): # Determine if this is a simulator using the multiarch value is_simulator = sys.implementation._multiarch.endswith("simulator") + is_catalyst = sys.implementation._multiarch.endswith("macabi") # We can't use ctypes; abort if not objc: @@ -68,4 +69,4 @@ def get_platform_ios(): release = objc.objc_msgSend(device_systemVersion, SEL_UTF8String).decode() model = objc.objc_msgSend(device_model, SEL_UTF8String).decode() - return system, release, model, is_simulator + return system, release, model, is_simulator, is_catalyst diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index d8a6f28edba39c..58642d961d6e2c 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1535,7 +1535,8 @@ def _get_supported_file_loaders(): """ extension_loaders = [] if hasattr(_imp, 'create_dynamic'): - if sys.platform in {"ios", "tvos", "watchos", "visionos"}: + # Mac Catalyst does NOT use the AppleFrameworkLoader. + if sys.platform in {"ios", "tvos", "watchos", "visionos"} and not sys.implementation._multiarch.endswith("macabi"): extension_loaders = [(AppleFrameworkLoader, [ suffix.replace(".so", ".fwork") for suffix in _imp.extension_suffixes() diff --git a/Lib/platform.py b/Lib/platform.py index cad919bc0c4969..c81849e870deb2 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -508,11 +508,11 @@ def mac_ver(release='', versioninfo=('', '', ''), machine=''): # A namedtuple for iOS version information. IOSVersionInfo = collections.namedtuple( "IOSVersionInfo", - ["system", "release", "model", "is_simulator"] + ["system", "release", "model", "is_simulator", "is_catalyst"] ) -def ios_ver(system="", release="", model="", is_simulator=False): +def ios_ver(system="", release="", model="", is_simulator=False, is_catalyst=False): """Get iOS version information, and return it as a namedtuple: (system, release, model, is_simulator). @@ -525,7 +525,7 @@ def ios_ver(system="", release="", model="", is_simulator=False): if result is not None: return IOSVersionInfo(*result) - return IOSVersionInfo(system, release, model, is_simulator) + return IOSVersionInfo(system, release, model, is_simulator, is_catalyst) # A namedtuple for tvOS version information. @@ -547,6 +547,7 @@ def tvos_ver(system="", release="", model="", is_simulator=False): import _ios_support result = _ios_support.get_platform_ios() if result is not None: + result = result[:-1] # ignore the Catalyst flag return TVOSVersionInfo(*result) return TVOSVersionInfo(system, release, model, is_simulator) @@ -571,6 +572,7 @@ def watchos_ver(system="", release="", model="", is_simulator=False): import _ios_support result = _ios_support.get_platform_ios() if result is not None: + result = result[:-1] # ignore the Catalyst flag return WatchOSVersionInfo(*result) return WatchOSVersionInfo(system, release, model, is_simulator) @@ -595,6 +597,7 @@ def visionos_ver(system="", release="", model="", is_simulator=False): import _ios_support result = _ios_support.get_platform_ios() if result is not None: + result = result[:-1] # ignore the Catalyst flag return VisionOSVersionInfo(*result) return VisionOSVersionInfo(system, release, model, is_simulator) @@ -968,7 +971,7 @@ def get_OpenVMS(): # there's only one CPU architecture for devices, so we know the right # answer. def get_ios(): - if sys.implementation._multiarch.endswith("simulator"): + if sys.implementation._multiarch.endswith("simulator") or sys.implementation._multiarch.endswith("macabi"): return os.uname().machine return 'arm64' @@ -1148,7 +1151,7 @@ def uname(): # Normalize responses on Apple mobile platforms if sys.platform == 'ios': - system, release, _, _ = ios_ver() + system, release, _, _, _ = ios_ver() if sys.platform == 'tvos': system, release, _, _ = tvos_ver() if sys.platform == 'watchos': @@ -1443,7 +1446,7 @@ def platform(aliased=False, terse=False): if system == 'Darwin': # macOS and iOS both report as a "Darwin" kernel if sys.platform == "ios": - system, release, _, _ = ios_ver() + system, release, _, _, _ = ios_ver() elif sys.platform == "tvos": system, release, _, _ = tvos_ver() elif sys.platform == "watchos": diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 03896a234bf6ef..24dd754db06dee 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -75,7 +75,10 @@ _mswindows = True # some platforms do not support subprocesses -_can_fork_exec = sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"} +_can_fork_exec = ( + sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"} + or sys.implementation._multiarch.endswith("macabi") +) if _mswindows: import _winapi diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 8594f92c097f07..3ae2728e2cfca9 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): - # Apple mobile platforms require the use of the custom framework loader, - # not the ExtensionFileLoader. - if support.is_apple_mobile: + # Apple mobile platforms EXCEPT Mac Catalyst require the use of the + # custom framework loader, not the ExtensionFileLoader. + if support.needs_apple_fworks: extension_loader = "AppleFrameworkLoader" else: extension_loader = "ExtensionFileLoader" diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 32243a49e7a407..4f027912b0a5a6 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -46,6 +46,7 @@ # sys "MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi", "is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval", + "is_mac_catalyst", "needs_apple_fworks", # os "get_pagesize", # network @@ -576,6 +577,8 @@ def skip_wasi_stack_overflow(): is_apple_mobile = sys.platform in {"ios", "tvos", "watchos", "visionos"} is_apple = is_apple_mobile or sys.platform == "darwin" +is_mac_catalyst = sys.implementation._multiarch.endswith("macabi") +needs_apple_fworks = is_apple_mobile and not is_mac_catalyst has_fork_support = hasattr(os, "fork") and not ( # WASM and Apple mobile platforms do not support subprocesses. @@ -586,6 +589,9 @@ def skip_wasi_stack_overflow(): # Although Android supports fork, it's unsafe to call it from Python because # all Android apps are multi-threaded. or is_android + + # Mac Catalyst supports subprocesses. + and not is_mac_catalyst ) def requires_fork(): @@ -601,6 +607,9 @@ def requires_fork(): # practice (see PEP 738). And most of the tests that use them are calling # sys.executable, which won't work when Python is embedded in an Android app. or is_android + + # Mac Catalyst supports subprocesses. + and not is_mac_catalyst ) def requires_subprocess(): diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index f74694a7a745a4..5ad6f4d4ff1fc5 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1920,7 +1920,7 @@ def test_module_state_shared_in_global(self): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if support.is_apple_mobile: + if support.needs_apple_fworks: loader = "AppleFrameworkLoader" else: loader = "ExtensionFileLoader" @@ -2604,7 +2604,7 @@ def setUp(self): origin = importlib.util.find_spec('_testmultiphase').origin # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if support.is_apple_mobile: + if support.needs_apple_fworks: loader = importlib.machinery.AppleFrameworkLoader(fullname, origin) else: loader = importlib.machinery.ExtensionFileLoader(fullname, origin) diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 6e34094c5aa422..87a81fdecfd927 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -33,7 +33,7 @@ swap_attr, swap_item, cpython_only, - is_apple_mobile, + needs_apple_fworks, is_emscripten, is_wasi, run_in_subinterp, @@ -111,7 +111,7 @@ def require_builtin(module, *, skip=False): def require_extension(module, *, skip=False): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if is_apple_mobile: + if needs_apple_fworks: _require_loader(module, AppleFrameworkLoader, skip) else: _require_loader(module, ExtensionFileLoader, skip) @@ -126,7 +126,7 @@ def require_pure_python(module, *, skip=False): def create_extension_loader(modname, filename): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if is_apple_mobile: + if needs_apple_fworks: return AppleFrameworkLoader(modname, filename) else: return ExtensionFileLoader(modname, filename) @@ -2217,7 +2217,7 @@ def import_script(self, name, fd, filename=None, check_override=None): if filename: # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if is_apple_mobile: + if needs_apple_fworks: loader = "AppleFrameworkLoader" else: loader = "ExtensionFileLoader" @@ -2692,7 +2692,7 @@ def setUpClass(cls): # Apple extensions must be distributed as frameworks. This requires # a specialist loader, and we need to differentiate between the # spec.origin and the original file location. - if is_apple_mobile: + if needs_apple_fworks: assert cls.LOADER is AppleFrameworkLoader cls.ORIGIN = spec.origin diff --git a/Lib/test/test_importlib/extension/test_finder.py b/Lib/test/test_importlib/extension/test_finder.py index cdc8884d668a66..d1b525f6419791 100644 --- a/Lib/test/test_importlib/extension/test_finder.py +++ b/Lib/test/test_importlib/extension/test_finder.py @@ -1,4 +1,4 @@ -from test.support import is_apple_mobile +from test.support import needs_apple_fworks from test.test_importlib import abc, util machinery = util.import_importlib('importlib.machinery') @@ -20,7 +20,7 @@ def setUp(self): ) def find_spec(self, fullname): - if is_apple_mobile: + if needs_apple_fworks: # Apple mobile platforms require a specialist loader that uses # .fwork files as placeholders for the true `.so` files. loaders = [ diff --git a/Lib/test/test_importlib/extension/test_loader.py b/Lib/test/test_importlib/extension/test_loader.py index 0dd21e079eba22..efae18d4bbd5c5 100644 --- a/Lib/test/test_importlib/extension/test_loader.py +++ b/Lib/test/test_importlib/extension/test_loader.py @@ -1,4 +1,4 @@ -from test.support import is_apple_mobile +from test.support import needs_apple_fworks from test.test_importlib import abc, util machinery = util.import_importlib('importlib.machinery') @@ -28,7 +28,7 @@ def setUp(self): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if is_apple_mobile: + if needs_apple_fworks: self.LoaderClass = self.machinery.AppleFrameworkLoader else: self.LoaderClass = self.machinery.ExtensionFileLoader @@ -110,7 +110,7 @@ def setUp(self): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if is_apple_mobile: + if needs_apple_fworks: self.LoaderClass = self.machinery.AppleFrameworkLoader else: self.LoaderClass = self.machinery.ExtensionFileLoader @@ -198,7 +198,7 @@ def setUp(self): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if is_apple_mobile: + if needs_apple_fworks: self.LoaderClass = self.machinery.AppleFrameworkLoader else: self.LoaderClass = self.machinery.ExtensionFileLoader diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 5de89714eb50c7..0c6661791cabcc 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -714,7 +714,7 @@ def test_single_phase_init_module(self): def test_incomplete_multi_phase_init_module(self): # Apple extensions must be distributed as frameworks. This requires # a specialist loader. - if support.is_apple_mobile: + if support.needs_apple_fworks: loader = "AppleFrameworkLoader" else: loader = "ExtensionFileLoader" diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index edbe78545a2536..d1f6965196fd3a 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -8,7 +8,7 @@ import os.path from test import support from test.support import import_helper -from test.support import is_apple_mobile +from test.support import needs_apple_fworks from test.support import os_helper import unittest import sys @@ -48,7 +48,7 @@ def _extension_details(): for ext in machinery.EXTENSION_SUFFIXES: # Apple mobile platforms mechanically load .so files, # but the findable files are labelled .fwork - if is_apple_mobile: + if needs_apple_fworks: ext = ext.replace(".so", ".fwork") filename = EXTENSIONS.name + ext diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 88b5b0e6e358bb..c0dfb3ae6d6618 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2510,19 +2510,51 @@ def test_isatty(self): @unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()') def test_closerange(self): - fd = os_helper.make_bad_fd() - # Make sure none of the descriptors we are about to close are - # currently valid (issue 6542). - for i in range(10): - try: os.fstat(fd+i) - except OSError: - pass - else: - break - if i < 2: - raise unittest.SkipTest( - "Unable to acquire a range of invalid file descriptors") - self.assertEqual(os.closerange(fd, fd + i-1), None) + if support.is_mac_catalyst: + # On Mac Catalyst, somehow there's a guarded FD in the way, + # undected using fstat. We make some random fds first, stop + # once not consecutive, close all of them one by one using + # the result of open(), then assert that closerange is failing. + # This ensures that none of those things we're closing is + # guarded, if we're careful to not use code that makes guarded + # file descriptors. + + copies = [] + # Open a file for testing and get its FD + file = open(os_helper.TESTFN, "wb") + fd = file.fileno() + copies.append(file) + for i in range(1, 10): + file_dup = open(os_helper.TESTFN, "wb") + if file_dup.fileno() != fd + i: + file_dup.close() + break + else: + copies.append(file_dup) + # Close everything + for copy in copies: + copy.close() + os.unlink(os_helper.TESTFN) + if i < 2: + raise unittest.SkipTest( + "Unable to acquire a range of invalid file descriptors") + + # Now we're left with invalid FDs. Let's go close them! + self.assertEqual(os.closerange(fd, fd + i-1), None) + else: + fd = os_helper.make_bad_fd() + # Make sure none of the descriptors we are about to close are + # currently valid (issue 6542). + for i in range(10): + try: os.fstat(fd+i) + except OSError: + pass + else: + break + if i < 2: + raise unittest.SkipTest( + "Unable to acquire a range of invalid file descriptors") + self.assertEqual(os.closerange(fd, fd + i-1), None) @unittest.skipUnless(hasattr(os, 'dup2'), 'test needs os.dup2()') def test_dup2(self): diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 92a831a9148cc4..e32d0bd8c16e3a 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -488,7 +488,7 @@ def test_ios_ver(self): # ios_ver is only fully available on iOS where ctypes is available. if sys.platform == "ios" and _ctypes: - system, release, model, is_simulator = result + system, release, model, is_simulator, is_catalyst = result # Result is a namedtuple self.assertEqual(result.system, system) self.assertEqual(result.release, release) @@ -499,6 +499,7 @@ def test_ios_ver(self): # ios_ver(), so we check that the values are broadly what we expect. # System is either iOS or iPadOS, depending on the test device + # Mac Catalyst returns iPadOS for whatever reason. self.assertIn(system, {"iOS", "iPadOS"}) # Release is a numeric version specifier with at least 2 parts @@ -511,6 +512,9 @@ def test_ios_ver(self): # we get a model descriptor like "iPhone13,1" if is_simulator: self.assertIn(model, {"iPhone", "iPad"}) + # Mac Catalyst identifies as iPad with no version. + elif is_catalyst: + self.assertEqual(model, "iPad") else: self.assertTrue( (model.startswith("iPhone") or model.startswith("iPad")) @@ -518,6 +522,10 @@ def test_ios_ver(self): ) self.assertEqual(type(is_simulator), bool) + + # Mac Catalyst platform will return iPadOS. + if is_catalyst: + self.assertEqual(system, "iPadOS") else: # On non-iOS platforms, calling ios_ver doesn't fail; you get # default values @@ -527,11 +535,12 @@ def test_ios_ver(self): self.assertFalse(result.is_simulator) # Check the fallback values can be overridden by arguments - override = platform.ios_ver("Foo", "Bar", "Whiz", True) + override = platform.ios_ver("Foo", "Bar", "Whiz", True, True) self.assertEqual(override.system, "Foo") self.assertEqual(override.release, "Bar") self.assertEqual(override.model, "Whiz") self.assertTrue(override.is_simulator) + self.assertTrue(override.is_catalyst) @unittest.skipIf(support.is_emscripten, "Does not apply to Emscripten") def test_libc_ver(self): diff --git a/MacCatalyst/Resources/Info.plist.in b/MacCatalyst/Resources/Info.plist.in new file mode 100644 index 00000000000000..80b5664508ec55 --- /dev/null +++ b/MacCatalyst/Resources/Info.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Python + CFBundleGetInfoString + Python Runtime and Library + CFBundleIdentifier + @PYTHONFRAMEWORKIDENTIFIER@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Python + CFBundlePackageType + FMWK + CFBundleShortVersionString + %VERSION% + CFBundleLongVersionString + %VERSION%, (c) 2001-2024 Python Software Foundation. + CFBundleSignature + ???? + CFBundleVersion + %VERSION% + CFBundleSupportedPlatforms + + MacOSX + + LSMinimumSystemVersion + @CATALYST_MACOS_VERSION@ + UIDeviceFamily + + 2 + + + diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar new file mode 100755 index 00000000000000..1e31b940c23c17 --- /dev/null +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-ar @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} ar "$@" diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang new file mode 100755 index 00000000000000..054392afd6bda2 --- /dev/null +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios-macabi "$@" diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ new file mode 100755 index 00000000000000..e1c46b204e9756 --- /dev/null +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target arm64-apple-ios-macabi "$@" diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp new file mode 100755 index 00000000000000..4db28ecf2194a5 --- /dev/null +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios-macabi -E "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar new file mode 100755 index 00000000000000..1e31b940c23c17 --- /dev/null +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-ar @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} ar "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang new file mode 100755 index 00000000000000..98543287e0545f --- /dev/null +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios-macabi "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ new file mode 100755 index 00000000000000..e9f063f559b80f --- /dev/null +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target x86_64-apple-ios-macabi "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp new file mode 100755 index 00000000000000..133a48a743069e --- /dev/null +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp @@ -0,0 +1,2 @@ +#!/bin/sh +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios-macabi -E "$@" diff --git a/MacCatalyst/Resources/pyconfig.h b/MacCatalyst/Resources/pyconfig.h new file mode 100644 index 00000000000000..4acff2c6051637 --- /dev/null +++ b/MacCatalyst/Resources/pyconfig.h @@ -0,0 +1,7 @@ +#ifdef __arm64__ +#include "pyconfig-arm64.h" +#endif + +#ifdef __x86_64__ +#include "pyconfig-x86_64.h" +#endif diff --git a/Makefile.pre.in b/Makefile.pre.in index e436b2efb8e205..d8df7de3102fbf 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2277,10 +2277,6 @@ testios: echo "Cannot run the iOS testbed for a non-iOS build."; \ exit 1;\ fi - @if test "$(findstring -iphonesimulator,$(MULTIARCH))" != "-iphonesimulator"; then \ - echo "Cannot run the iOS testbed for non-simulator builds."; \ - exit 1;\ - fi @if test $(PYTHONFRAMEWORK) != "Python"; then \ echo "Cannot run the iOS testbed with a non-default framework name."; \ exit 1;\ @@ -2290,11 +2286,18 @@ testios: exit 1;\ fi - # 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 + @if test "$(findstring -iphonesimulator,$(MULTIARCH))" != "-iphonesimulator"; then \ + if test "$(findstring -macabi,$(MULTIARCH))" != "-macabi"; then \ + echo "Cannot run the iOS testbed for device builds."; \ + exit 1;\ + else \ + $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed --catalyst clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)"; \ + $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" --catalyst run --verbose -- test -uall --single-process --rerun -W; \ + fi \ + else \ + $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER-iOS)"; \ + $(PYTHON_FOR_BUILD) "$(XCFOLDER-iOS)" run --verbose -- test -uall --single-process --rerun -W; \ + fi # Run the test suite on the visionOS simulator. Must be run on a macOS machine with # a full Xcode install that has an Apple Vision Pro simulator available. @@ -3013,7 +3016,7 @@ frameworkinstallversionedstructure: $(LDLIBRARY) fi; \ done $(LN) -fsn include/python$(LDVERSION) $(DESTDIR)$(prefix)/Headers - sed 's/%VERSION%/'"`$(RUNSHARED) ./$(BUILDPYTHON) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist + sed 's/%VERSION%/'"`$(RUNSHARED) ./$(PYTHON_FOR_BUILD) -c 'import platform; print(platform.python_version())'`"'/g' < $(RESSRCDIR)/Info.plist > $(DESTDIR)$(prefix)/Resources/Info.plist $(LN) -fsn $(VERSION) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Versions/Current $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/$(PYTHONFRAMEWORK) $(LN) -fsn Versions/Current/Headers $(DESTDIR)$(PYTHONFRAMEWORKINSTALLDIR)/Headers @@ -3041,6 +3044,14 @@ frameworkinstallunversionedstructure: $(LDLIBRARY) $(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \ done +# Stub compilation assistance binaries are installed separately on Mac Catalyst. +.PHONY: frameworkinstallcatalyststubs +frameworkinstallcatalyststubs: $(LDLIBRARY) + $(INSTALL) -d -m $(DIRMODE) $(DESTDIR)$(BINDIR) + for file in $(srcdir)/$(RESSRCDIR)/bin/* ; do \ + $(INSTALL) -m $(EXEMODE) $$file $(DESTDIR)$(BINDIR); \ + done + # This installs Mac/Lib into the framework # Install a number of symlinks to keep software that expects a normal unix # install (which includes python-config) happy. diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index 6c1863c943b353..ba634ae16990d4 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -254,6 +254,12 @@ PLATFORM_TRIPLET=x86_64-iphonesimulator # else PLATFORM_TRIPLET=arm64-iphonesimulator # endif +# elif defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST +# if __x86_64__ +PLATFORM_TRIPLET=x86_64-iphoneos-macabi +# else +PLATFORM_TRIPLET=arm64-iphoneos-macabi +# endif # else PLATFORM_TRIPLET=arm64-iphoneos # endif diff --git a/config.sub b/config.sub index 49febd56a37cc0..6efa3fbf821859 100755 --- a/config.sub +++ b/config.sub @@ -1769,7 +1769,7 @@ case $os in | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ - | fiwix* | mlibc* | cos* | mbr* | ironclad* ) + | fiwix* | mlibc* | cos* | mbr* | ironclad* | macabi) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) @@ -1869,6 +1869,8 @@ case $kernel-$os-$obj in ;; ios*-simulator- | tvos*-simulator- | watchos*-simulator- | xros*-simulator-) ;; + ios*-macabi- ) + ;; none--*) # None (no kernel, i.e. freestanding / bare metal), # can be paired with an machine code file format diff --git a/configure b/configure index 7c93d36e71729b..64b6e3ce2b3db6 100755 --- a/configure +++ b/configure @@ -982,6 +982,7 @@ LDFLAGS CFLAGS CC HAS_XCRUN +CATALYST_MACOS_VERSION EXPORT_XROS_DEPLOYMENT_TARGET XROS_DEPLOYMENT_TARGET WATCHOS_DEPLOYMENT_TARGET @@ -1089,6 +1090,7 @@ with_universal_archs with_framework_name enable_framework with_app_store_compliance +with_catalyst_macos_version enable_wasm_dynamic_linking enable_wasm_pthreads with_suffix @@ -1878,6 +1880,9 @@ Optional Packages: Enable any patches required for compiliance with app stores. Optional PATCH-FILE specifies the custom patch to apply. + --with-catalyst-macos-version=VER + The minimum macOS version a Catalyst build can run + on (only valid for *-apple-ios*-macabi targets) --with-suffix=SUFFIX set executable suffix to SUFFIX (default is empty, yes is mapped to '.exe') --without-static-libpython @@ -4222,9 +4227,12 @@ fi # configure will fail. if test -z "$AR"; then case "$host" in + x86_64-apple-ios*-macabi) AR=x86_64-apple-ios-macabi-ar ;; + aarch64-apple-ios*-macabi) AR=arm64-apple-ios-macabi-ar ;; + + x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; - x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; @@ -4241,9 +4249,12 @@ if test -z "$AR"; then fi if test -z "$CC"; then case "$host" in + x86_64-apple-ios*-macabi) CC=x86_64-apple-ios-macabi-clang ;; + aarch64-apple-ios*-macabi) CC=arm64-apple-ios-macabi-clang ;; + + x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; - x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; @@ -4260,9 +4271,12 @@ if test -z "$CC"; then fi if test -z "$CPP"; then case "$host" in + x86_64-apple-ios*-macabi) CPP=x86_64-apple-ios-macabi-cpp ;; + aarch64-apple-ios*-macabi) CPP=arm64-apple-ios-macabi-cpp ;; + + x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; - x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; @@ -4279,9 +4293,12 @@ if test -z "$CPP"; then fi if test -z "$CXX"; then case "$host" in + x86_64-apple-ios*-macabi) CXX=x86_64-apple-ios-macabi-clang++ ;; + aarch64-apple-ios*-macabi) CXX=arm64-apple-ios-macabi-clang++ ;; + + x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; - x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; @@ -4524,19 +4541,35 @@ then : ;; iOS) : - FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" - FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " - FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" - FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" - FRAMEWORKPYTHONW= - INSTALLTARGETS="libinstall inclinstall sharedinstall" - - prefix=$PYTHONFRAMEWORKPREFIX - PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" - RESSRCDIR=iOS/Resources - - ac_config_files="$ac_config_files iOS/Resources/Info.plist" + _flag_ios_catalyst=`echo $host | cut -d '-' -f4` + case $_flag_ios_catalyst in + macabi) + FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs" + FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs " + FRAMEWORKINSTALLLAST="" + FRAMEWORKALTINSTALLLAST="" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" + prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR/Versions/$VERSION" + RESSRCDIR=MacCatalyst/Resources + ac_config_files="$ac_config_files MacCatalyst/Resources/Info.plist" + + ;; + *) + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + RESSRCDIR=iOS/Resources + ac_config_files="$ac_config_files iOS/Resources/Info.plist" + ;; + esac ;; tvOS) : FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" @@ -4688,6 +4721,38 @@ fi + + +# Check whether --with-catalyst-macos-version was given. +if test ${with_catalyst_macos_version+y} +then : + withval=$with_catalyst_macos_version; case "$host" in + *-apple-ios*-macabi) + CATALYST_MACOS_VERSION="$withval" + ;; + *) + as_fn_error $? "--with-catalyst-macos-version is only valid when targeting Mac Catalyst (*-apple-ios*-macabi)." "$LINENO" 5 + ;; + esac + +else case e in #( + e) case "$host" in + *-apple-ios*-macabi) + CATALYST_MACOS_VERSION=11.2 + ;; + *) + CATALYST_MACOS_VERSION= + ;; + esac + + ;; +esac +fi + + + + + EXPORT_XROS_DEPLOYMENT_TARGET='#' @@ -4705,6 +4770,28 @@ if test "$cross_compiling" = yes; then *-*-cygwin*) _host_ident= ;; + *-apple-ios*-macabi) + _host_os=`echo $host | cut -d '-' -f3` + _host_device=`echo $host | cut -d '-' -f4` # should be macabi + _host_device=${_host_device:=os} + + # IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking iOS deployment target" >&5 +printf %s "checking iOS deployment target... " >&6; } + IPHONEOS_DEPLOYMENT_TARGET=$(echo ${_host_os} | cut -c4-) + IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=14.2} # else it returns invalid version number + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $IPHONEOS_DEPLOYMENT_TARGET" >&5 +printf "%s\n" "$IPHONEOS_DEPLOYMENT_TARGET" >&6; } + + case "$host_cpu" in + aarch64) + _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphoneos-${_host_device} # platform_triplet.c uses iphoneos-macabi + ;; + *) + _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphoneos-${_host_device} + ;; + esac + ;; *-apple-ios*) _host_os=`echo $host | cut -d '-' -f3` _host_device=`echo $host | cut -d '-' -f4` @@ -4969,6 +5056,10 @@ EXPORT_MACOSX_DEPLOYMENT_TARGET='#' # XROS_DEPLOYMENT_TARGET should get exported +# The minimum macOS version that a Mac Catalyst build can run on. + + + # checks for alternative programs # compiler flags are generated in two sets, BASECFLAGS and OPT. OPT is just @@ -7459,6 +7550,10 @@ case $host/$ac_cv_cc_name in #( PY_SUPPORT_TIER=3 ;; #( x86_64-*-freebsd*/clang) : PY_SUPPORT_TIER=3 ;; #( + aarch64-apple-ios*-macabi/clang) : + PY_SUPPORT_TIER=3 ;; #( + x86_64-apple-ios*-macabi/clang) : + PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*-simulator/clang) : PY_SUPPORT_TIER=3 ;; #( aarch64-apple-ios*/clang) : @@ -31344,6 +31439,13 @@ case $ac_sys_system in #( ;; #( iOS|tvOS|watchOS|visionOS) : + case "$_host_device" in + macabi) ;; + *) + + py_cv_module__posixsubprocess=n/a + ;; + esac py_cv_module__curses=n/a @@ -31351,7 +31453,6 @@ case $ac_sys_system in #( py_cv_module__gdbm=n/a py_cv_module__multiprocessing=n/a py_cv_module__posixshmem=n/a - py_cv_module__posixsubprocess=n/a py_cv_module__scproxy=n/a py_cv_module__tkinter=n/a py_cv_module_grp=n/a @@ -35503,6 +35604,7 @@ do "Mac/PythonLauncher/Makefile") CONFIG_FILES="$CONFIG_FILES Mac/PythonLauncher/Makefile" ;; "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; + "MacCatalyst/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES MacCatalyst/Resources/Info.plist" ;; "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; "tvOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES tvOS/Resources/Info.plist" ;; "watchOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES watchOS/Resources/Info.plist" ;; diff --git a/configure.ac b/configure.ac index 7ab0609bf8ab3e..71957cbbaaed37 100644 --- a/configure.ac +++ b/configure.ac @@ -426,9 +426,12 @@ AC_SUBST([host_exec_prefix]) # configure will fail. if test -z "$AR"; then case "$host" in + x86_64-apple-ios*-macabi) AR=x86_64-apple-ios-macabi-ar ;; + aarch64-apple-ios*-macabi) AR=arm64-apple-ios-macabi-ar ;; + + x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; aarch64-apple-ios*-simulator) AR=arm64-apple-ios-simulator-ar ;; aarch64-apple-ios*) AR=arm64-apple-ios-ar ;; - x86_64-apple-ios*-simulator) AR=x86_64-apple-ios-simulator-ar ;; aarch64-apple-tvos*-simulator) AR=arm64-apple-tvos-simulator-ar ;; aarch64-apple-tvos*) AR=arm64-apple-tvos-ar ;; @@ -445,9 +448,12 @@ if test -z "$AR"; then fi if test -z "$CC"; then case "$host" in + x86_64-apple-ios*-macabi) CC=x86_64-apple-ios-macabi-clang ;; + aarch64-apple-ios*-macabi) CC=arm64-apple-ios-macabi-clang ;; + + x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; aarch64-apple-ios*-simulator) CC=arm64-apple-ios-simulator-clang ;; aarch64-apple-ios*) CC=arm64-apple-ios-clang ;; - x86_64-apple-ios*-simulator) CC=x86_64-apple-ios-simulator-clang ;; aarch64-apple-tvos*-simulator) CC=arm64-apple-tvos-simulator-clang ;; aarch64-apple-tvos*) CC=arm64-apple-tvos-clang ;; @@ -464,9 +470,12 @@ if test -z "$CC"; then fi if test -z "$CPP"; then case "$host" in + x86_64-apple-ios*-macabi) CPP=x86_64-apple-ios-macabi-cpp ;; + aarch64-apple-ios*-macabi) CPP=arm64-apple-ios-macabi-cpp ;; + + x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; aarch64-apple-ios*-simulator) CPP=arm64-apple-ios-simulator-cpp ;; aarch64-apple-ios*) CPP=arm64-apple-ios-cpp ;; - x86_64-apple-ios*-simulator) CPP=x86_64-apple-ios-simulator-cpp ;; aarch64-apple-tvos*-simulator) CPP=arm64-apple-tvos-simulator-cpp ;; aarch64-apple-tvos*) CPP=arm64-apple-tvos-cpp ;; @@ -483,9 +492,12 @@ if test -z "$CPP"; then fi if test -z "$CXX"; then case "$host" in + x86_64-apple-ios*-macabi) CXX=x86_64-apple-ios-macabi-clang++ ;; + aarch64-apple-ios*-macabi) CXX=arm64-apple-ios-macabi-clang++ ;; + + x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*-simulator) CXX=arm64-apple-ios-simulator-clang++ ;; aarch64-apple-ios*) CXX=arm64-apple-ios-clang++ ;; - x86_64-apple-ios*-simulator) CXX=x86_64-apple-ios-simulator-clang++ ;; aarch64-apple-tvos*-simulator) CXX=arm64-apple-tvos-simulator-clang++ ;; aarch64-apple-tvos*) CXX=arm64-apple-tvos-clang++ ;; @@ -716,18 +728,33 @@ AC_ARG_ENABLE([framework], AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) ;; iOS) : - FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" - FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " - FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" - FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" - FRAMEWORKPYTHONW= - INSTALLTARGETS="libinstall inclinstall sharedinstall" - - prefix=$PYTHONFRAMEWORKPREFIX - PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" - RESSRCDIR=iOS/Resources - - AC_CONFIG_FILES([iOS/Resources/Info.plist]) + _flag_ios_catalyst=`echo $host | cut -d '-' -f4` + case $_flag_ios_catalyst in + macabi) + FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs" + FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs " + FRAMEWORKINSTALLLAST="" + FRAMEWORKALTINSTALLLAST="" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" + prefix=$PYTHONFRAMEWORKINSTALLDIR/Versions/$VERSION + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR/Versions/$VERSION" + RESSRCDIR=MacCatalyst/Resources + AC_CONFIG_FILES([MacCatalyst/Resources/Info.plist]) + ;; + *) + FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" + FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " + FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" + FRAMEWORKPYTHONW= + INSTALLTARGETS="libinstall inclinstall sharedinstall" + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" + RESSRCDIR=iOS/Resources + AC_CONFIG_FILES([iOS/Resources/Info.plist]) + ;; + esac ;; tvOS) : FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" @@ -863,6 +890,33 @@ AC_ARG_WITH( ]) AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) + +AC_ARG_WITH([catalyst-macos-version], + [AS_HELP_STRING([--with-catalyst-macos-version=VER], + [The minimum macOS version a Catalyst build can run on (only valid for *-apple-ios*-macabi targets)])], + [case "$host" in + *-apple-ios*-macabi) + CATALYST_MACOS_VERSION="$withval" + ;; + *) + AC_MSG_ERROR([--with-catalyst-macos-version is only valid when targeting Mac Catalyst (*-apple-ios*-macabi).]) + ;; + esac + ], + [case "$host" in + *-apple-ios*-macabi) + CATALYST_MACOS_VERSION=11.2 + ;; + *) + CATALYST_MACOS_VERSION= + ;; + esac + ] +) + + + + EXPORT_XROS_DEPLOYMENT_TARGET='#' AC_SUBST([_PYTHON_HOST_PLATFORM]) @@ -880,6 +934,26 @@ if test "$cross_compiling" = yes; then *-*-cygwin*) _host_ident= ;; + *-apple-ios*-macabi) + _host_os=`echo $host | cut -d '-' -f3` + _host_device=`echo $host | cut -d '-' -f4` # should be macabi + _host_device=${_host_device:=os} + + # IPHONEOS_DEPLOYMENT_TARGET is the minimum supported iOS version + AC_MSG_CHECKING([iOS deployment target]) + IPHONEOS_DEPLOYMENT_TARGET=$(echo ${_host_os} | cut -c4-) + IPHONEOS_DEPLOYMENT_TARGET=${IPHONEOS_DEPLOYMENT_TARGET:=14.2} # else it returns invalid version number + AC_MSG_RESULT([$IPHONEOS_DEPLOYMENT_TARGET]) + + case "$host_cpu" in + aarch64) + _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-arm64-iphoneos-${_host_device} # platform_triplet.c uses iphoneos-macabi + ;; + *) + _host_ident=${IPHONEOS_DEPLOYMENT_TARGET}-$host_cpu-iphoneos-${_host_device} + ;; + esac + ;; *-apple-ios*) _host_os=`echo $host | cut -d '-' -f3` _host_device=`echo $host | cut -d '-' -f4` @@ -1129,6 +1203,10 @@ AC_SUBST([XROS_DEPLOYMENT_TARGET]) # XROS_DEPLOYMENT_TARGET should get exported AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET]) +# The minimum macOS version that a Mac Catalyst build can run on. +AC_SUBST([CATALYST_MACOS_VERSION]) + + # checks for alternative programs # compiler flags are generated in two sets, BASECFLAGS and OPT. OPT is just @@ -1418,6 +1496,8 @@ AS_CASE([$host/$ac_cv_cc_name], [powerpc64le-*-linux-gnu/clang], [PY_SUPPORT_TIER=3], dnl Linux on PPC64 little endian, glibc, clang [s390x-*-linux-gnu/gcc], [PY_SUPPORT_TIER=3], dnl Linux on 64bit s390x (big endian), glibc, gcc [x86_64-*-freebsd*/clang], [PY_SUPPORT_TIER=3], dnl FreeBSD on AMD64 + [aarch64-apple-ios*-macabi/clang], [PY_SUPPORT_TIER=3], dnl MacCatalyst on arm64 + [x86_64-apple-ios*-macabi/clang], [PY_SUPPORT_TIER=3], dnl MacCatalyst on x86_64 [aarch64-apple-ios*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64 @@ -7932,13 +8012,17 @@ AS_CASE([$ac_sys_system], dnl curses and tkinter user interface are not available. dnl gdbm and nis aren't available dnl Stub implementations are provided for pwd, grp etc APIs + dnl subprocess is however supported for Mac Catalyst + case "$_host_device" in + macabi) ;; + *) PY_STDLIB_MOD_SET_NA([_posixsubprocess]) ;; + esac PY_STDLIB_MOD_SET_NA( [_curses], [_curses_panel], [_gdbm], [_multiprocessing], [_posixshmem], - [_posixsubprocess], [_scproxy], [_tkinter], [grp], diff --git a/iOS/testbed/Python.xcframework/Info.plist b/iOS/testbed/Python.xcframework/Info.plist index c6418de6e74a4e..7b32df19ab4ffc 100644 --- a/iOS/testbed/Python.xcframework/Info.plist +++ b/iOS/testbed/Python.xcframework/Info.plist @@ -4,6 +4,23 @@ AvailableLibraries + + BinaryPath + Python.framework/Versions/Latest/Python + LibraryIdentifier + ios-arm64_x86_64-maccatalyst + LibraryPath + python.framework + SupportedArchitectures + + x86_64 + arm64 + + SupportedPlatform + ios + SupportedPlatformVariant + maccatalyst + BinaryPath Python.framework/Python diff --git a/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README new file mode 100644 index 00000000000000..b39f40980bc874 --- /dev/null +++ b/iOS/testbed/Python.xcframework/ios-arm64_x86_64-maccatalyst/README @@ -0,0 +1,4 @@ +This directory is intentionally empty. + +It is a placeholder slice for the XCFramework on Mac Catalyst, +to install or copy your built framework to. diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py index 1146bf3b988cda..2bee1f54206578 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py @@ -28,6 +28,15 @@ r"\s+\(Python\)\s" # Logger name ) +# Prefix: 2025-04-27 21:43:38.530606-0500 iOSTestbed[96892:48053672] +CATALYST_LOG_PREFIX_REGEX = re.compile( + r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD + r"\s+\d{2}:\d{2}:\d{2}\.\d{6}" # HH:MM:SS.ssssss (microseconds, 6 digits) + r"[-+]\d{4}" # Timezone offset like -0500 + r"\s+iOSTestbed\[\d+:\d+\] " # iOSTestbed[ProcessID:ThreadID] (both numbers), then a space +) + + # Work around a bug involving sys.exit and TaskGroups # (https://github.com/python/cpython/issues/101515). @@ -243,9 +252,13 @@ async def log_stream_task(initial_devices, lock): sys.stdout.flush() -async def xcode_test(location, simulator, verbose): +async def xcode_test(location, simulator, verbose, catalyst): # Run the test suite on the named simulator print("Starting xcodebuild...", flush=True) + if catalyst: + destination_arg = "platform=macOS,variant=Mac Catalyst" + else: + destination_arg = f"platform=iOS Simulator,name={simulator}"; args = [ "xcodebuild", "test", @@ -254,13 +267,13 @@ async def xcode_test(location, simulator, verbose): "-scheme", "iOSTestbed", "-destination", - f"platform=iOS Simulator,name={simulator}", + destination_arg, "-resultBundlePath", str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), "-derivedDataPath", str(location / "DerivedData"), ] - if not verbose: + if not verbose and not catalyst: args += ["-quiet"] async with async_process( @@ -269,8 +282,16 @@ async def xcode_test(location, simulator, verbose): stderr=subprocess.STDOUT, ) as process: while line := (await process.stdout.readline()).decode(*DECODE_ARGS): - sys.stdout.write(line) - sys.stdout.flush() + # For Mac Catalyst, the *actual* logs are streamed here. Only stream + # things that does NOT come from the process when verbose. + if catalyst: + if CATALYST_LOG_PREFIX_REGEX.match(line) or verbose: + line = CATALYST_LOG_PREFIX_REGEX.sub("", line) + sys.stdout.write(line) + sys.stdout.flush() + else: + sys.stdout.write(line) + sys.stdout.flush() status = await asyncio.wait_for(process.wait(), timeout=1) exit(status) @@ -281,20 +302,31 @@ def clone_testbed( target: Path, framework: Path, apps: list[Path], + catalyst: bool, ) -> None: if target.exists(): print(f"{target} already exists; aborting without creating project.") sys.exit(10) if framework is None: - if not ( - source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" - ).is_dir(): - print( - f"The testbed being cloned ({source}) does not contain " - f"a simulator framework. Re-run with --framework" - ) - sys.exit(11) + if catalyst: + if not ( + source / "Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework/Versions" + ).is_dir(): + print( + f"The testbed being cloned ({source}) does not contain " + f"a Mac Catalyst framework. Re-run with --framework" + ) + sys.exit(11) + else: + if not ( + source / "Python.xcframework/ios-arm64_x86_64-simulator/bin" + ).is_dir(): + print( + f"The testbed being cloned ({source}) does not contain " + f"a simulator framework. Re-run with --framework" + ) + sys.exit(11) else: if not framework.is_dir(): print(f"{framework} does not exist.") @@ -305,7 +337,7 @@ def clone_testbed( ): print( f"{framework} is not an XCframework, " - f"or a simulator slice of a framework build." + f"or a simulator / Catalyst slice of a framework build." ) sys.exit(13) @@ -315,7 +347,10 @@ def clone_testbed( print(" done") xc_framework_path = target / "Python.xcframework" - sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" + if catalyst: + sim_framework_path = xc_framework_path / "ios-arm64_x86_64-maccatalyst" + else: + sim_framework_path = xc_framework_path / "ios-arm64_x86_64-simulator" if framework is not None: if framework.suffix == ".xcframework": print(" Installing XCFramework...", end="", flush=True) @@ -328,7 +363,7 @@ def clone_testbed( ) print(" done") else: - print(" Installing simulator framework...", end="", flush=True) + print(" Installing simulator/catalyst framework...", end="", flush=True) if sim_framework_path.is_dir(): shutil.rmtree(sim_framework_path) else: @@ -360,7 +395,7 @@ def clone_testbed( sim_framework_path.is_symlink() and not sim_framework_path.readlink().is_absolute() ): - print(" Rewriting symlink to simulator framework...", end="", flush=True) + print(" Rewriting symlink to simulator/catalyst framework...", end="", flush=True) # Simulator framework is a relative symlink. Rewrite the symlink # relative to the new location. orig_sim_framework_path = ( @@ -401,42 +436,52 @@ def update_plist(testbed_path, args): plistlib.dump(info, f) -async def run_testbed(simulator: str | None, args: list[str], verbose: bool=False): +async def run_testbed(simulator: str | None, args: list[str], catalyst: bool, verbose: bool=False): location = Path(__file__).parent print("Updating plist...", end="", flush=True) update_plist(location, args) print(" done.", flush=True) - if simulator is None: - simulator = await select_simulator_device() - print(f"Running test on {simulator}", flush=True) - - # We need to get an exclusive lock on simulator creation, to avoid issues - # with multiple simulators starting and being unable to tell which - # simulator is due to which testbed instance. See - # https://github.com/python/cpython/issues/130294 for details. Wait up to - # 10 minutes for a simulator to boot. - print("Obtaining lock on simulator creation...", flush=True) - simulator_lock = SimulatorLock(timeout=10*60) - await simulator_lock.acquire() - print("Simulator lock acquired.", flush=True) - - # Get the list of devices that are booted at the start of the test run. - # The simulator started by the test suite will be detected as the new - # entry that appears on the device list. - initial_devices = await list_devices() - - try: - async with asyncio.TaskGroup() as tg: - tg.create_task(log_stream_task(initial_devices, simulator_lock)) - tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose)) - except* MySystemExit as e: - raise SystemExit(*e.exceptions[0].args) from None - except* subprocess.CalledProcessError as e: - # Extract it from the ExceptionGroup so it can be handled by `main`. - raise e.exceptions[0] - finally: - simulator_lock.release() + if not catalyst: + if simulator is None: + simulator = await select_simulator_device() + print(f"Running test on {simulator}", flush=True) + + # We need to get an exclusive lock on simulator creation, to avoid issues + # with multiple simulators starting and being unable to tell which + # simulator is due to which testbed instance. See + # https://github.com/python/cpython/issues/130294 for details. Wait up to + # 10 minutes for a simulator to boot. + print("Obtaining lock on simulator creation...", flush=True) + simulator_lock = SimulatorLock(timeout=10*60) + await simulator_lock.acquire() + print("Simulator lock acquired.", flush=True) + + # Get the list of devices that are booted at the start of the test run. + # The simulator started by the test suite will be detected as the new + # entry that appears on the device list. + initial_devices = await list_devices() + + try: + async with asyncio.TaskGroup() as tg: + tg.create_task(log_stream_task(initial_devices, simulator_lock)) + tg.create_task(xcode_test(location, simulator=simulator, verbose=verbose, catalyst=False)) + except* MySystemExit as e: + raise SystemExit(*e.exceptions[0].args) from None + except* subprocess.CalledProcessError as e: + # Extract it from the ExceptionGroup so it can be handled by `main`. + raise e.exceptions[0] + finally: + simulator_lock.release() + else: + try: + async with asyncio.TaskGroup() as tg: + tg.create_task(xcode_test(location, simulator="", verbose=verbose, catalyst=True)) + except* MySystemExit as e: + raise SystemExit(*e.exceptions[0].args) from None + except* subprocess.CalledProcessError as e: + # Extract it from the ExceptionGroup so it can be handled by `main`. + raise e.exceptions[0] def main(): @@ -448,6 +493,12 @@ def main(): subcommands = parser.add_subparsers(dest="subcommand") + parser.add_argument( + "--catalyst", + action="store_true", + help="Use Mac Catalyst.", + ) + clone = subcommands.add_parser( "clone", description=( @@ -514,11 +565,16 @@ def main(): target=Path(context.location).resolve(), framework=Path(context.framework).resolve() if context.framework else None, apps=[Path(app) for app in context.apps], + catalyst = context.catalyst ) elif context.subcommand == "run": if test_args: + if context.catalyst: + expected_location = "Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework" + else: + expected_location = "Python.xcframework/ios-arm64_x86_64-simulator/bin" if not ( - Path(__file__).parent / "Python.xcframework/ios-arm64_x86_64-simulator/bin" + Path(__file__).parent / expected_location ).is_dir(): print( f"Testbed does not contain a compiled iOS framework. Use " @@ -530,8 +586,10 @@ def main(): asyncio.run( run_testbed( simulator=context.simulator, + # Mac catalyst requires verbose, or no logs will print. verbose=context.verbose, args=test_args, + catalyst=context.catalyst ) ) else: diff --git a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj index c7d63909ee2453..ace08f7efd97fc 100644 --- a/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj +++ b/iOS/testbed/iOSTestbed.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = ""; }; 608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = ""; }; 608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = ""; }; + EE325B2A2DBE97CD000142D0 /* iOSTestbed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iOSTestbed.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -115,6 +116,7 @@ 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { isa = PBXGroup; children = ( + EE325B2A2DBE97CD000142D0 /* iOSTestbed.entitlements */, 608619552CB7819B00F46182 /* app */, 608619532CB77BA900F46182 /* app_packages */, 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, @@ -262,7 +264,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\nmkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelse\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nfi\n"; + shellScript = "set -e\n\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-iphonesimulator\" ]; then\n mkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\n echo \"Installing Python modules for iOS Simulator\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\" \nelif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-maccatalyst\" ]; then\n mkdir -p \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib\"\n echo \"Installing Python modules for Mac Catalyst\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-maccatalyst/Python.framework/Versions/3.14/lib/\" \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib/\"\nelse\n mkdir -p \"$CODESIGNING_FOLDER_PATH/python/lib\"\n echo \"Installing Python modules for iOS Device\"\n rsync -au --delete \"$PROJECT_DIR/Python.xcframework/ios-arm64/lib/\" \"$CODESIGNING_FOLDER_PATH/python/lib/\"\nfi\n"; showEnvVarsInLog = 0; }; 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */ = { @@ -282,7 +284,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n"; + shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nif [ \"$EFFECTIVE_PLATFORM_NAME\" = \"-maccatalyst\" ]; then\n PYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib\")\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/app_packages\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n find \"$CODESIGNING_FOLDER_PATH/Contents/Resources/app\" -name \"*.so\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der {} \\;\n \nelse\n PYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\n echo \"Install Python $PYTHON_VER standard library extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\n done\n echo \"Install app package extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\n done\n echo \"Install app extension modules...\"\n find \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\n done\n\n # Clean up dylib template \n rm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\n echo \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\n find \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -362,6 +364,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -381,6 +384,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -423,6 +427,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -436,6 +441,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -449,9 +455,12 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CODE_SIGN_ENTITLEMENTS = iOSTestbed/iOSTestbed.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; INFOPLIST_FILE = "iOSTestbed/iOSTestbed-Info.plist"; @@ -468,6 +477,9 @@ MARKETING_VERSION = 3.13.0a1; PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -479,9 +491,12 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CODE_SIGN_ENTITLEMENTS = iOSTestbed/iOSTestbed.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; @@ -499,6 +514,9 @@ MARKETING_VERSION = 3.13.0a1; PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = YES; SWIFT_EMIT_LOC_STRINGS = YES; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -509,9 +527,10 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 3HEZE76D99; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; IPHONEOS_DEPLOYMENT_TARGET = 12.0; @@ -529,9 +548,10 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 3HEZE76D99; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; IPHONEOS_DEPLOYMENT_TARGET = 12.0; diff --git a/iOS/testbed/iOSTestbed/iOSTestbed.entitlements b/iOS/testbed/iOSTestbed/iOSTestbed.entitlements new file mode 100644 index 00000000000000..8cc185af8d78a2 --- /dev/null +++ b/iOS/testbed/iOSTestbed/iOSTestbed.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.disable-library-validation + + + From 7c7a4cfe46a5c8de8d3034a47afd0987320b858f Mon Sep 17 00:00:00 2001 From: John Date: Mon, 9 Jun 2025 17:29:34 -0500 Subject: [PATCH 5/8] Apply suggestions from code review --- Lib/importlib/_bootstrap_external.py | 2 +- Lib/platform.py | 2 +- Lib/subprocess.py | 2 +- Lib/test/datetimetester.py | 2 +- Lib/test/test_os.py | 13 ++++++++----- Lib/test/test_platform.py | 2 +- .../Resources/bin/arm64-apple-ios-macabi-clang | 2 +- .../Resources/bin/arm64-apple-ios-macabi-clang++ | 2 +- .../Resources/bin/arm64-apple-ios-macabi-cpp | 2 +- .../Resources/bin/x86_64-apple-ios-macabi-clang | 2 +- .../Resources/bin/x86_64-apple-ios-macabi-clang++ | 2 +- .../Resources/bin/x86_64-apple-ios-macabi-cpp | 2 +- configure.ac | 7 +------ iOS/testbed/__main__.py | 1 - 14 files changed, 20 insertions(+), 23 deletions(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 58642d961d6e2c..dfab36488f6943 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1535,7 +1535,7 @@ def _get_supported_file_loaders(): """ extension_loaders = [] if hasattr(_imp, 'create_dynamic'): - # Mac Catalyst does NOT use the AppleFrameworkLoader. + # AppleFrameworkLoader is unessaccary on Mac Catalyst because Mac Catalyst allows "raw" dylib files. if sys.platform in {"ios", "tvos", "watchos", "visionos"} and not sys.implementation._multiarch.endswith("macabi"): extension_loaders = [(AppleFrameworkLoader, [ suffix.replace(".so", ".fwork") diff --git a/Lib/platform.py b/Lib/platform.py index c81849e870deb2..756f9e41cb02cc 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -1444,7 +1444,7 @@ def platform(aliased=False, terse=False): system, release, version = system_alias(system, release, version) if system == 'Darwin': - # macOS and iOS both report as a "Darwin" kernel + # All Apple Platforms report as a "Darwin" kernel if sys.platform == "ios": system, release, _, _, _ = ios_ver() elif sys.platform == "tvos": diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 24dd754db06dee..9b3f9f612e9581 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -77,7 +77,7 @@ # some platforms do not support subprocesses _can_fork_exec = ( sys.platform not in {"emscripten", "wasi", "ios", "tvos", "watchos", "visionos"} - or sys.implementation._multiarch.endswith("macabi") + or sys.implementation._multiarch.endswith("macabi") # Mac Catalyst ) if _mswindows: diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 3ae2728e2cfca9..7eecf926082995 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -7159,7 +7159,7 @@ def test_datetime_from_timestamp(self): self.assertEqual(dt_orig, dt_rt) def test_type_check_in_subinterp(self): - # Apple mobile platforms EXCEPT Mac Catalyst require the use of the + # Apple mobile platforms except Mac Catalyst require the use of the # custom framework loader, not the ExtensionFileLoader. if support.needs_apple_fworks: extension_loader = "AppleFrameworkLoader" diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index c0dfb3ae6d6618..c5121fee00e761 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2511,13 +2511,16 @@ def test_isatty(self): @unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()') def test_closerange(self): if support.is_mac_catalyst: - # On Mac Catalyst, somehow there's a guarded FD in the way, - # undected using fstat. We make some random fds first, stop - # once not consecutive, close all of them one by one using - # the result of open(), then assert that closerange is failing. + # On Mac Catalyst and potentially other Apple platforms, + # somehow there's a guarded FD in the way, that has no + # effect on fstat. We make some random FDs first, stop + # once not consecutive, close all of them one by one by calling + # file.close(), then assert that closerange on the FDs is failing. # This ensures that none of those things we're closing is # guarded, if we're careful to not use code that makes guarded - # file descriptors. + # file descriptors between when we manually made and + # destroyed them and when we're attempting to invoke + # closerange. copies = [] # Open a file for testing and get its FD diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index e32d0bd8c16e3a..2f4f221fe4faed 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -499,7 +499,7 @@ def test_ios_ver(self): # ios_ver(), so we check that the values are broadly what we expect. # System is either iOS or iPadOS, depending on the test device - # Mac Catalyst returns iPadOS for whatever reason. + # Mac Catalyst returns iPadOS. self.assertIn(system, {"iOS", "iPadOS"}) # Release is a numeric version specifier with at least 2 parts diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang index 054392afd6bda2..6845b5d1b20c8d 100755 --- a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang @@ -1,2 +1,2 @@ #!/bin/sh -xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios-macabi "$@" +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@" diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ index e1c46b204e9756..779c3166268b08 100755 --- a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-clang++ @@ -1,2 +1,2 @@ #!/bin/sh -xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target arm64-apple-ios-macabi "$@" +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@" diff --git a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp index 4db28ecf2194a5..01ff56a0f0ed03 100755 --- a/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp +++ b/MacCatalyst/Resources/bin/arm64-apple-ios-macabi-cpp @@ -1,2 +1,2 @@ #!/bin/sh -xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios-macabi -E "$@" +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi -E "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang index 98543287e0545f..a3170bb1f1553c 100755 --- a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang @@ -1,2 +1,2 @@ #!/bin/sh -xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios-macabi "$@" +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ index e9f063f559b80f..d3161b335e1605 100755 --- a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-clang++ @@ -1,2 +1,2 @@ #!/bin/sh -xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target x86_64-apple-ios-macabi "$@" +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang++ -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi "$@" diff --git a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp index 133a48a743069e..7e3e9124123e62 100755 --- a/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp +++ b/MacCatalyst/Resources/bin/x86_64-apple-ios-macabi-cpp @@ -1,2 +1,2 @@ #!/bin/sh -xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios-macabi -E "$@" +xcrun --sdk macosx${MACOSX_SDK_VERSION} clang -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-macabi -E "$@" diff --git a/configure.ac b/configure.ac index 71957cbbaaed37..1c47cbef67da84 100644 --- a/configure.ac +++ b/configure.ac @@ -728,8 +728,7 @@ AC_ARG_ENABLE([framework], AC_CONFIG_FILES([Mac/Resources/app/Info.plist]) ;; iOS) : - _flag_ios_catalyst=`echo $host | cut -d '-' -f4` - case $_flag_ios_catalyst in + case $(echo $host | cut -d '-' -f4) in macabi) FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs" FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs " @@ -913,10 +912,6 @@ AC_ARG_WITH([catalyst-macos-version], esac ] ) - - - - EXPORT_XROS_DEPLOYMENT_TARGET='#' AC_SUBST([_PYTHON_HOST_PLATFORM]) diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py index 2bee1f54206578..93656e038a4d23 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py @@ -586,7 +586,6 @@ def main(): asyncio.run( run_testbed( simulator=context.simulator, - # Mac catalyst requires verbose, or no logs will print. verbose=context.verbose, args=test_args, catalyst=context.catalyst From e0c6f8abcc7e340865bd49031018044c750ae8a8 Mon Sep 17 00:00:00 2001 From: John Zhou Date: Mon, 9 Jun 2025 17:30:32 -0500 Subject: [PATCH 6/8] autoregen --- configure | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/configure b/configure index 64b6e3ce2b3db6..5710b745618708 100755 --- a/configure +++ b/configure @@ -4541,8 +4541,7 @@ then : ;; iOS) : - _flag_ios_catalyst=`echo $host | cut -d '-' -f4` - case $_flag_ios_catalyst in + case $(echo $host | cut -d '-' -f4) in macabi) FRAMEWORKINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs" FRAMEWORKALTINSTALLFIRST="frameworkinstallversionedstructure frameworkinstallcatalyststubs " @@ -4749,10 +4748,6 @@ else case e in #( esac fi - - - - EXPORT_XROS_DEPLOYMENT_TARGET='#' From b21f164136c6c7b9c2c5d4afdf0b947ea5834cb9 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 9 Jun 2025 17:31:28 -0500 Subject: [PATCH 7/8] Update platform.py --- Lib/platform.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/platform.py b/Lib/platform.py index 756f9e41cb02cc..37dd6ce7bf1595 100644 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -966,10 +966,10 @@ def get_OpenVMS(): csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) return 'Alpha' if cpu_number >= 128 else 'VAX' - # 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. + # On the iOS/tvOS/watchOS/visionOS simulator and Mac Catalyst, os.uname returns + # the architecture as uname.machine. On device it returns the model name for + # some reason; but there's only one CPU architecture for devices, so we know the + # right answer. def get_ios(): if sys.implementation._multiarch.endswith("simulator") or sys.implementation._multiarch.endswith("macabi"): return os.uname().machine From 769dbf34c041d3e35655c4f5dabc9b7de3dde7f7 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 11 Jun 2025 20:00:46 -0500 Subject: [PATCH 8/8] a help change --- iOS/testbed/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/testbed/__main__.py b/iOS/testbed/__main__.py index 93656e038a4d23..1c42b07aebde75 100644 --- a/iOS/testbed/__main__.py +++ b/iOS/testbed/__main__.py @@ -496,7 +496,7 @@ def main(): parser.add_argument( "--catalyst", action="store_true", - help="Use Mac Catalyst.", + help="Run or clone the testbed for a Mac Catalyst build.", ) clone = subcommands.add_parser(