From db3b09740a1323251cd3149be3720f96a6eb006f Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Thu, 18 Sep 2025 13:46:09 +0100 Subject: [PATCH] Bump to Python 3.14.0rc3. --- .github/workflows/ci.yaml | 18 +- Makefile | 60 +- patch/Python/Python.patch | 9639 ++++++++++++++++++++++++++----------- 3 files changed, 6836 insertions(+), 2881 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e69d4c1..e5c63bb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,6 +29,9 @@ on: XZ_VERSION: description: "The XZ version used for the build." value: ${{ jobs.config.outputs.XZ_VERSION }} + ZSTD_VERSION: + description: "The Zstandard version used for the build." + value: ${{ jobs.config.outputs.ZSTD_VERSION }} env: FORCE_COLOR: "1" @@ -54,6 +57,7 @@ jobs: MPDECIMAL_VERSION: ${{ steps.extract.outputs.MPDECIMAL_VERSION }} OPENSSL_VERSION: ${{ steps.extract.outputs.OPENSSL_VERSION }} XZ_VERSION: ${{ steps.extract.outputs.XZ_VERSION }} + ZSTD_VERSION: ${{ steps.extract.outputs.ZSTD_VERSION }} steps: - uses: actions/checkout@v5 @@ -68,6 +72,7 @@ jobs: MPDECIMAL_VERSION=$(make config | grep "MPDECIMAL_VERSION=" | cut -d "=" -f 2) OPENSSL_VERSION=$(make config | grep "OPENSSL_VERSION=" | cut -d "=" -f 2) XZ_VERSION=$(make config | grep "XZ_VERSION=" | cut -d "=" -f 2) + ZSTD_VERSION=$(make config | grep "ZSTD_VERSION=" | cut -d "=" -f 2) if [ -z "${{ inputs.build-number }}" ]; then BUILD_NUMBER=custom else @@ -82,6 +87,7 @@ jobs: echo "MPDECIMAL_VERSION=${MPDECIMAL_VERSION}" | tee -a ${GITHUB_OUTPUT} echo "OPENSSL_VERSION=${OPENSSL_VERSION}" | tee -a ${GITHUB_OUTPUT} echo "XZ_VERSION=${XZ_VERSION}" | tee -a ${GITHUB_OUTPUT} + echo "ZSTD_VERSION=${ZSTD_VERSION}" | tee -a ${GITHUB_OUTPUT} build: runs-on: macOS-15 @@ -184,16 +190,16 @@ jobs: cpython-testbed: name: CPython testbed (${{ matrix.platform }}) - # For now, CPython testbed can't run on macos-15: https://github.com/actions/runner-images/issues/12777 - runs-on: macOS-14 + runs-on: macOS-15 needs: [ config, build ] strategy: fail-fast: false matrix: - platform: ["iOS", "visionOS"] + platform: ["iOS", "tvOS", "visionOS"] include: - - platform: "iOS" - testbed-args: '--simulator "iPhone 16e,arch=arm64,OS=18.5"' + # Needed to disambiguate simulator options. + - platform: "visionOS" + testbed-args: '--simulator "Apple Vision Pro,arch=arm64,OS=2.5"' steps: - uses: actions/checkout@v5 @@ -231,7 +237,7 @@ jobs: # - test_os as a test of system library calls # - test_bz2 as a simple test of third party libraries # - test_ctypes as a test of FFI - python -m testbed run -- test --single-process --rerun -W test_builtin test_grammar test_os test_bz2 test_ctypes + python -m testbed run --verbose ${{ matrix.testbed-args }} -- test --single-process --rerun -W test_builtin test_grammar test_os test_bz2 test_ctypes crossenv-test: name: Cross-platform env test (${{ matrix.multiarch }}) diff --git a/Makefile b/Makefile index 3579649..6923b42 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ BUILD_NUMBER=custom # of a release cycle, as official binaries won't be published. # PYTHON_MICRO_VERSION is the full version number, without any alpha/beta/rc suffix. (e.g., 3.10.0) # PYTHON_VER is the major/minor version (e.g., 3.10) -PYTHON_VERSION=3.14.0rc1 +PYTHON_VERSION=3.14.0rc3 PYTHON_PKG_VERSION=$(PYTHON_VERSION) PYTHON_MICRO_VERSION=$(shell echo $(PYTHON_VERSION) | grep -Eo "\d+\.\d+\.\d+") PYTHON_PKG_MICRO_VERSION=$(shell echo $(PYTHON_PKG_VERSION) | grep -Eo "\d+\.\d+\.\d+") @@ -32,6 +32,7 @@ LIBFFI_VERSION=3.4.7-2 MPDECIMAL_VERSION=4.0.0-2 OPENSSL_VERSION=3.0.16-2 XZ_VERSION=5.6.4-2 +ZSTD_VERSION=1.5.7-1 # Supported OS OS_LIST=macOS iOS tvOS watchOS visionOS @@ -41,25 +42,30 @@ CURL_FLAGS=--disable --fail --location --create-dirs --progress-bar # macOS targets TARGETS-macOS=macosx.x86_64 macosx.arm64 TRIPLE_OS-macOS=macos +PLATFORM_NAME-macOS=macOS VERSION_MIN-macOS=11.0 # iOS targets TARGETS-iOS=iphonesimulator.x86_64 iphonesimulator.arm64 iphoneos.arm64 TRIPLE_OS-iOS=ios +PLATFORM_NAME-iOS=iOS VERSION_MIN-iOS=13.0 # tvOS targets TARGETS-tvOS=appletvsimulator.x86_64 appletvsimulator.arm64 appletvos.arm64 TRIPLE_OS-tvOS=tvos +PLATFORM_NAME-tvOS=tvOS VERSION_MIN-tvOS=12.0 # watchOS targets TARGETS-watchOS=watchsimulator.x86_64 watchsimulator.arm64 watchos.arm64_32 TRIPLE_OS-watchOS=watchos +PLATFORM_NAME-watchOS=watchOS VERSION_MIN-watchOS=4.0 TARGETS-visionOS=xrsimulator.arm64 xros.arm64 TRIPLE_OS-visionOS=xros +PLATFORM_NAME-visionOS=xrOS VERSION_MIN-visionOS=2.0 # The architecture of the machine doing the build @@ -95,7 +101,7 @@ update-patch: # call if [ -z "$(PYTHON_REPO_DIR)" ]; then echo "\n\nPYTHON_REPO_DIR must be set to the root of your Python github checkout\n\n"; fi cd $(PYTHON_REPO_DIR) && \ - git diff -D v$(PYTHON_VERSION) $(PYTHON_VER)-patched \ + git diff --no-renames -D v$(PYTHON_VERSION) $(PYTHON_VER)-patched \ | PATH="/usr/local/bin:/opt/homebrew/bin:$(PATH)" filterdiff \ -X $(PROJECT_DIR)/patch/Python/diff.exclude -p 1 --clean \ > $(PROJECT_DIR)/patch/Python/Python.patch @@ -187,6 +193,26 @@ $$(XZ_LIB-$(target)): downloads/xz-$(XZ_VERSION)-$(target).tar.gz # Ensure the target is marked as clean. touch $$(XZ_LIB-$(target)) +########################################################################### +# Target: zstd (ZStandard) +########################################################################### + +ZSTD_INSTALL-$(target)=$(PROJECT_DIR)/install/$(os)/$(target)/zstd-$(ZSTD_VERSION) +ZSTD_LIB-$(target)=$$(ZSTD_INSTALL-$(target))/lib/libzstd.a + +downloads/zstd-$(ZSTD_VERSION)-$(target).tar.gz: + @echo ">>> Download zstd for $(target)" + mkdir -p downloads + curl $(CURL_FLAGS) -o $$@ \ + https://github.com/beeware/cpython-apple-source-deps/releases/download/zstd-$(ZSTD_VERSION)/zstd-$(ZSTD_VERSION)-$(target).tar.gz + +$$(ZSTD_LIB-$(target)): downloads/zstd-$(ZSTD_VERSION)-$(target).tar.gz + @echo ">>> Install zstd for $(target)" + mkdir -p $$(ZSTD_INSTALL-$(target)) + cd $$(ZSTD_INSTALL-$(target)) && tar zxvf $(PROJECT_DIR)/downloads/zstd-$(ZSTD_VERSION)-$(target).tar.gz --exclude="*.dylib" + # Ensure the target is marked as clean. + touch $$(ZSTD_LIB-$(target)) + ########################################################################### # Target: mpdecimal ########################################################################### @@ -280,14 +306,15 @@ $$(PYTHON_SRCDIR-$(target))/configure: \ $$(LIBFFI_LIB-$(target)) \ $$(MPDECIMAL_LIB-$(target)) \ $$(OPENSSL_SSL_LIB-$(target)) \ - $$(XZ_LIB-$(target)) + $$(XZ_LIB-$(target)) \ + $$(ZSTD_LIB-$(target)) @echo ">>> Unpack and configure Python for $(target)" mkdir -p $$(PYTHON_SRCDIR-$(target)) tar zxf downloads/Python-$(PYTHON_VERSION).tar.gz --strip-components 1 -C $$(PYTHON_SRCDIR-$(target)) # Apply target Python patches cd $$(PYTHON_SRCDIR-$(target)) && patch -p1 < $(PROJECT_DIR)/patch/Python/Python.patch # Make sure the binary scripts are executable - chmod 755 $$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin/* + chmod 755 $$(PYTHON_SRCDIR-$(target))/Apple/$(os)/Resources/bin/* # Touch the configure script to ensure that Make identifies it as up to date. touch $$(PYTHON_SRCDIR-$(target))/configure @@ -295,7 +322,7 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ $$(PYTHON_SRCDIR-$(target))/configure # Configure target Python cd $$(PYTHON_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/Apple/$(os)/Resources/bin:$(PATH)" \ ./configure \ LIBLZMA_CFLAGS="-I$$(XZ_INSTALL-$(target))/include" \ LIBLZMA_LIBS="-L$$(XZ_INSTALL-$(target))/lib -llzma" \ @@ -305,6 +332,8 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ LIBMPDEC_LIBS="-L$$(MPDECIMAL_INSTALL-$(target))/lib -lmpdec" \ LIBFFI_CFLAGS="-I$$(LIBFFI_INSTALL-$(target))/include" \ LIBFFI_LIBS="-L$$(LIBFFI_INSTALL-$(target))/lib -lffi" \ + LIBZSTD_CFLAGS="-I$$(ZSTD_INSTALL-$(target))/include" \ + LIBZSTD_LIBS="-L$$(ZSTD_INSTALL-$(target))/lib -lzstd" \ --host=$$(TARGET_TRIPLE-$(target)) \ --build=$(HOST_ARCH)-apple-darwin \ --with-build-python=$(HOST_PYTHON) \ @@ -317,14 +346,14 @@ $$(PYTHON_SRCDIR-$(target))/Makefile: \ $$(PYTHON_SRCDIR-$(target))/python.exe: $$(PYTHON_SRCDIR-$(target))/Makefile @echo ">>> Build Python for $(target)" cd $$(PYTHON_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/Apple/$(os)/Resources/bin:$(PATH)" \ make -j8 all \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).build.log $$(PYTHON_LIB-$(target)): $$(PYTHON_SRCDIR-$(target))/python.exe @echo ">>> Install Python for $(target)" cd $$(PYTHON_SRCDIR-$(target)) && \ - PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/$(os)/Resources/bin:$(PATH)" \ + PATH="$(PROJECT_DIR)/$$(PYTHON_SRCDIR-$(target))/Apple/$(os)/Resources/bin:$(PATH)" \ make install \ 2>&1 | tee -a ../python-$(PYTHON_VERSION).install.log @@ -490,15 +519,11 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk)) mkdir -p $$(PYTHON_INSTALL-$(sdk))/include ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER) -ifeq ($(os), visionOS) - echo "Skipping arch-specific header copying for visionOS" -else # Add the individual headers from each target in an arch-specific name $$(foreach target,$$(SDK_TARGETS-$(sdk)),cp $$(PYTHON_INCLUDE-$$(target))/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig-$$(ARCH-$$(target)).h; ) # Copy the cross-target header from the source folder of the first target in the $(sdk) SDK - cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/$(os)/Resources/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h -endif + cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$(sdk))))/Apple/$(os)/Resources/pyconfig.h $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h $$(PYTHON_STDLIB-$(sdk))/LICENSE.TXT: $$(PYTHON_LIB-$(sdk)) $$(PYTHON_FRAMEWORK-$(sdk))/Info.plist $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h $$(foreach target,$$(SDK_TARGETS-$(sdk)),$$(PYTHON_PLATFORM_SITECUSTOMIZE-$$(target))) @@ -666,6 +691,11 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ -output $$(PYTHON_XCFRAMEWORK-$(os)) $$(foreach sdk,$$(SDKS-$(os)),-framework $$(PYTHON_FRAMEWORK-$$(sdk))) \ 2>&1 | tee -a support/$(PYTHON_VER)/python-$(os).xcframework.log + @echo ">>> Install build tools for $(os)" + mkdir $$(PYTHON_XCFRAMEWORK-$(os))/build + cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/Apple/testbed/Python.xcframework/build/utils.sh $$(PYTHON_XCFRAMEWORK-$(os))/build + cp $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/Apple/testbed/Python.xcframework/build/$$(PLATFORM_NAME-$(os))-dylib-Info-template.plist $$(PYTHON_XCFRAMEWORK-$(os))/build + @echo ">>> Install PYTHONHOME for $(os)" $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/include $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/bin $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) @@ -674,9 +704,9 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \ # Disable dSYM production (for now) # $$(foreach sdk,$$(SDKS-$(os)),cp -r $$(PYTHON_INSTALL-$$(sdk))/Python.dSYM $$(PYTHON_XCFRAMEWORK-$(os))/$$(SDK_SLICE-$$(sdk)); ) -ifeq ($(filter $(os),iOS visionOS),$(os)) +ifeq ($(filter $(os),iOS tvOS visionOS),$(os)) @echo ">>> Clone testbed project for $(os)" - $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/$(os)/testbed clone --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed + $(HOST_PYTHON) $$(PYTHON_SRCDIR-$$(firstword $$(SDK_TARGETS-$$(firstword $$(SDKS-$(os))))))/Apple/testbed clone --platform $(os) --framework $$(PYTHON_XCFRAMEWORK-$(os)) support/$(PYTHON_VER)/$(os)/testbed endif @echo ">>> Create VERSIONS file for $(os)" @@ -689,6 +719,7 @@ endif echo "mpdecimal: $(MPDECIMAL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "OpenSSL: $(OPENSSL_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS echo "XZ: $(XZ_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS + echo "Zstandard: $(ZSTD_VERSION)" >> support/$(PYTHON_VER)/$(os)/VERSIONS dist/Python-$(PYTHON_VER)-$(os)-support.$(BUILD_NUMBER).tar.gz: \ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist \ @@ -757,6 +788,7 @@ config: @echo "MPDECIMAL_VERSION=$(MPDECIMAL_VERSION)" @echo "OPENSSL_VERSION=$(OPENSSL_VERSION)" @echo "XZ_VERSION=$(XZ_VERSION)" + @echo "ZSTD_VERSION=$(ZSTD_VERSION)" # Expand cross-platform build and clean targets for each output product clean: $(foreach os,$(OS_LIST),clean-$(os)) diff --git a/patch/Python/Python.patch b/patch/Python/Python.patch index f9f6d8e..89f2a54 100644 --- a/patch/Python/Python.patch +++ b/patch/Python/Python.patch @@ -1,1973 +1,1396 @@ -diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py -index 823a3692fd1..00639dd8488 100644 ---- a/Lib/ctypes/__init__.py -+++ b/Lib/ctypes/__init__.py -@@ -419,7 +419,7 @@ - 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 99504911a3d..527c2f36dd0 100644 ---- a/Lib/ctypes/util.py -+++ b/Lib/ctypes/util.py -@@ -126,7 +126,7 @@ - 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 @@ - # 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 8bcd741c446..d8a6f28edba 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 @@ - """ - 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 7dd64de6025..a7e3c77f803 100644 ---- a/Lib/platform.py -+++ b/Lib/platform.py -@@ -528,6 +528,78 @@ - return IOSVersionInfo(system, release, model, is_simulator) - - -+# A namedtuple for tvOS version information. -+TVOSVersionInfo = collections.namedtuple( -+ "TVOSVersionInfo", -+ ["system", "release", "model", "is_simulator"] -+) +--- /dev/null ++++ b/Apple/__main__.py +@@ -0,0 +1,1057 @@ ++#!/usr/bin/env python3 ++########################################################################## ++# Apple XCframework build script ++# ++# This script simplifies the process of configuring, compiling and packaging an ++# XCframework for an Apple platform. ++# ++# At present, it supports iOS, tvOS, visionOS and watchOS, but it has been ++# constructed so that it could be used on any Apple platform. ++# ++# The simplest entry point is: ++# ++# $ python Apple ci iOS ++# ++# (replace iOS with tvOS, visionOS or watchOS as required.) ++# ++# which will: ++# * Clean any pre-existing build artefacts ++# * Configure and make a Python that can be used for the build ++# * Configure and make a Python for each supported iOS/tvOS architecture and ABI ++# * Combine the outputs of the builds from the previous step into a single ++# XCframework, merging binaries into a "fat" binary if necessary ++# * Clone a copy of the testbed, configured to use the XCframework ++# * Construct a tarball containing the release artefacts ++# * Run the test suite using the generated XCframework. ++# ++# This is the complete sequence that would be needed in CI to build and test ++# a candidate release artefact. ++# ++# Each individual step can be invoked individually - there are commands to ++# clean, configure-build, make-build, configure-host, make-host, package, and ++# test. ++# ++# There is also a build command that can be used to combine the configure and ++# make steps for the build Python, an individual host, all hosts, or all ++# builds. ++########################################################################## ++from __future__ import annotations ++ ++import argparse ++import os ++import platform ++import re ++import shlex ++import shutil ++import signal ++import subprocess ++import sys ++import sysconfig ++import time ++from collections.abc import Sequence ++from contextlib import contextmanager ++from datetime import datetime, timezone ++from os.path import basename, relpath ++from pathlib import Path ++from subprocess import CalledProcessError ++from typing import Callable ++ ++EnvironmentT = dict[str, str] ++ArgsT = Sequence[str | Path] ++ ++SCRIPT_NAME = Path(__file__).name ++PYTHON_DIR = Path(__file__).resolve().parent.parent ++ ++CROSS_BUILD_DIR = PYTHON_DIR / "cross-build" ++ ++HOSTS: dict[str, dict[str, dict[str, str]]] = { ++ # Structure of this data: ++ # * Platform identifier ++ # * an XCframework slice that must exist for that platform ++ # * a host triple: the multiarch spec for that host ++ "iOS": { ++ "ios-arm64": { ++ "arm64-apple-ios": "arm64-iphoneos", ++ }, ++ "ios-arm64_x86_64-simulator": { ++ "arm64-apple-ios-simulator": "arm64-iphonesimulator", ++ "x86_64-apple-ios-simulator": "x86_64-iphonesimulator", ++ }, ++ }, ++ "tvOS": { ++ "tvos-arm64": { ++ "arm64-apple-tvos": "arm64-appletvos", ++ }, ++ "tvos-arm64_x86_64-simulator": { ++ "arm64-apple-tvos-simulator": "arm64-appletvsimulator", ++ "x86_64-apple-tvos-simulator": "x86_64-appletvsimulator", ++ }, ++ }, ++ "visionOS": { ++ "xros-arm64": { ++ "arm64-apple-xros": "arm64-xros", ++ }, ++ "xros-arm64-simulator": { ++ "arm64-apple-xros-simulator": "arm64-xrsimulator", ++ }, ++ }, ++ "watchOS": { ++ "watchos-arm64_32": { ++ "arm64_32-apple-watchos": "arm64_32-watchos", ++ }, ++ "watchos-arm64_x86_64-simulator": { ++ "arm64-apple-watchos-simulator": "arm64-watchsimulator", ++ "x86_64-apple-watchos-simulator": "x86_64-watchsimulator", ++ }, ++ }, ++} + + -+def tvos_ver(system="", release="", model="", is_simulator=False): -+ """Get tvOS version information, and return it as a namedtuple: -+ (system, release, model, is_simulator). ++def subdir(name: str, create: bool = False) -> Path: ++ """Ensure that a cross-build directory for the given name exists.""" ++ path = CROSS_BUILD_DIR / name ++ if not path.exists(): ++ if not create: ++ sys.exit( ++ f"{path} does not exist. Create it by running the appropriate " ++ f"`configure` subcommand of {SCRIPT_NAME}." ++ ) ++ else: ++ path.mkdir(parents=True) ++ return path + -+ If values can't be determined, they are set to values provided as -+ parameters. ++ ++def run( ++ command: ArgsT, ++ *, ++ host: str | None = None, ++ env: EnvironmentT | None = None, ++ log: bool | None = True, ++ **kwargs, ++) -> subprocess.CompletedProcess: ++ """Run a command in an Apple development environment. ++ ++ Optionally logs the executed command to the console. + """ -+ 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) ++ kwargs.setdefault("check", True) ++ if env is None: ++ env = os.environ.copy() + -+ return TVOSVersionInfo(system, release, model, is_simulator) ++ if host: ++ host_env = apple_env(host) ++ print_env(host_env) ++ env.update(host_env) + ++ if log: ++ print(">", join_command(command)) ++ return subprocess.run(command, env=env, **kwargs) + -+# A namedtuple for watchOS version information. -+WatchOSVersionInfo = collections.namedtuple( -+ "WatchOSVersionInfo", -+ ["system", "release", "model", "is_simulator"] -+) + ++def join_command(args: str | Path | ArgsT) -> str: ++ """Format a command so it can be copied into a shell. + -+def watchos_ver(system="", release="", model="", is_simulator=False): -+ """Get watchOS version information, and return it as a namedtuple: -+ (system, release, model, is_simulator). ++ Similar to `shlex.join`, but also accepts arguments which are Paths, or a ++ single string/Path outside of a list. ++ """ ++ if isinstance(args, (str, Path)): ++ return str(args) ++ else: ++ return shlex.join(map(str, args)) ++ ++ ++def print_env(env: EnvironmentT) -> None: ++ """Format the environment so it can be pasted into a shell.""" ++ for key, value in sorted(env.items()): ++ print(f"export {key}={shlex.quote(value)}") ++ ++ ++def platform_for_host(host): ++ """Determine the platform for a given host triple.""" ++ for plat, slices in HOSTS.items(): ++ for _, candidates in slices.items(): ++ for candidate in candidates: ++ if candidate == host: ++ return plat ++ raise KeyError(host) ++ ++ ++def apple_env(host: str) -> EnvironmentT: ++ """Construct an Apple development environment for the given host.""" ++ env = { ++ "PATH": ":".join( ++ [ ++ str( ++ PYTHON_DIR ++ / f"Apple/{platform_for_host(host)}/Resources/bin" ++ ), ++ str(subdir(host) / "prefix"), ++ "/usr/bin", ++ "/bin", ++ "/usr/sbin", ++ "/sbin", ++ "/Library/Apple/usr/bin", ++ ] ++ ), ++ } + -+ If values can't be determined, they are set to values provided as -+ parameters. ++ return env ++ ++ ++def delete_path(name: str) -> None: ++ """Delete the named cross-build directory, if it exists.""" ++ path = CROSS_BUILD_DIR / name ++ if path.exists(): ++ print(f"Deleting {path} ...") ++ shutil.rmtree(path) ++ ++ ++def all_host_triples(platform: str) -> list[str]: ++ """Return all host triples for the given platform. ++ ++ The host triples are the platform definitions used as input to configure ++ (e.g., "arm64-apple-ios-simulator"). + """ -+ 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) ++ triples = [] ++ for slice_name, slice_parts in HOSTS[platform].items(): ++ triples.extend(list(slice_parts)) ++ return triples ++ ++ ++def clean(context: argparse.Namespace, target: str = "all") -> None: ++ """The implementation of the "clean" command.""" ++ # If we're explicitly targeting the build, there's no platform or ++ # distribution artefacts. If we're cleaning tests, we keep all built ++ # artefacts. Otherwise, the built artefacts must be dirty, so we remove ++ # them. ++ if target not in {"build", "test"}: ++ paths = ["dist", context.platform] + list(HOSTS[context.platform]) ++ else: ++ paths = [] ++ ++ if target in {"all", "build"}: ++ paths.append("build") ++ ++ if target in {"all", "hosts"}: ++ paths.extend(all_host_triples(context.platform)) ++ elif target not in {"build", "test", "package"}: ++ paths.append(target) ++ ++ if target in {"all", "hosts", "test"}: ++ paths.extend( ++ [ ++ path.name ++ for path in CROSS_BUILD_DIR.glob( ++ f"{context.platform}-testbed.*" ++ ) ++ ] ++ ) + -+ return WatchOSVersionInfo(system, release, model, is_simulator) ++ for path in paths: ++ delete_path(path) + + -+# A namedtuple for visionOS version information. -+VisionOSVersionInfo = collections.namedtuple( -+ "VisionOSVersionInfo", -+ ["system", "release", "model", "is_simulator"] -+) ++def build_python_path() -> Path: ++ """The path to the build Python binary.""" ++ build_dir = subdir("build") ++ binary = build_dir / "python" ++ if not binary.is_file(): ++ binary = binary.with_suffix(".exe") ++ if not binary.is_file(): ++ raise FileNotFoundError( ++ f"Unable to find `python(.exe)` in {build_dir}" ++ ) + ++ return binary + -+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. ++@contextmanager ++def group(text: str): ++ """A context manager that outputs a log marker around a section of a build. ++ ++ If running in a GitHub Actions environment, the GitHub syntax for ++ collapsible log sections is used. + """ -+ 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) ++ if "GITHUB_ACTIONS" in os.environ: ++ print(f"::group::{text}") ++ else: ++ print(f"===== {text} " + "=" * (70 - len(text))) + -+ return VisionOSVersionInfo(system, release, model, is_simulator) ++ yield + ++ if "GITHUB_ACTIONS" in os.environ: ++ print("::endgroup::") ++ else: ++ print() + - def _java_getprop(name, default): - """This private helper is deprecated in 3.13 and will be removed in 3.15""" - from java.lang import System -@@ -727,7 +799,7 @@ - default in case the command should fail. - - """ -- if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos'}: -+ if sys.platform in {'dos', 'win32', 'win16', 'ios', 'tvos', 'watchos', 'visionos'}: - # XXX Others too ? - return default - -@@ -891,14 +963,30 @@ - csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) - return 'Alpha' if cpu_number >= 128 else 'VAX' - -- # On the iOS simulator, os.uname returns the architecture as uname.machine. -- # On device it returns the model name for some reason; but there's only one -- # CPU architecture for iOS devices, so we know the right answer. -+ # On the iOS/tvOS/watchOS/visionOS simulator, os.uname returns the architecture as -+ # uname.machine. On device it returns the model name for some reason; but -+ # there's only one CPU architecture for devices, so we know the right -+ # answer. - def get_ios(): - if sys.implementation._multiarch.endswith("simulator"): - return os.uname().machine - return 'arm64' - -+ def get_tvos(): -+ if sys.implementation._multiarch.endswith("simulator"): -+ return os.uname().machine -+ return 'arm64' + -+ def get_watchos(): -+ if sys.implementation._multiarch.endswith("simulator"): -+ return os.uname().machine -+ return 'arm64_32' ++@contextmanager ++def cwd(subdir: Path): ++ """A context manager that sets the current working directory.""" ++ orig = os.getcwd() ++ os.chdir(subdir) ++ yield ++ os.chdir(orig) + -+ def get_visionos(): -+ if sys.implementation._multiarch.endswith("simulator"): -+ return os.uname().machine -+ return 'arm64' + - def from_subprocess(): - """ - Fall back to `uname -p` -@@ -1058,9 +1146,15 @@ - system = 'Android' - release = android_ver().release - -- # Normalize responses on iOS -+ # Normalize responses on Apple mobile platforms - if sys.platform == 'ios': - system, release, _, _ = ios_ver() -+ if sys.platform == 'tvos': -+ system, release, _, _ = tvos_ver() -+ if sys.platform == 'watchos': -+ system, release, _, _ = watchos_ver() -+ if sys.platform == 'visionos': -+ system, release, _, _ = visionos_ver() - - vals = system, node, release, version, machine - # Replace 'unknown' values with the more portable '' -@@ -1350,6 +1444,12 @@ - # macOS and iOS both report as a "Darwin" kernel - if sys.platform == "ios": - system, release, _, _ = ios_ver() -+ elif sys.platform == "tvos": -+ system, release, _, _ = tvos_ver() -+ elif sys.platform == "watchos": -+ system, release, _, _ = watchos_ver() -+ elif sys.platform == "visionos": -+ system, release, _, _ = visionos_ver() - else: - macos_release = mac_ver()[0] - if macos_release: -diff --git a/Lib/site.py b/Lib/site.py -index f9327197159..74899abecb0 100644 ---- a/Lib/site.py -+++ b/Lib/site.py -@@ -298,8 +298,8 @@ - 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 54c2eb515b6..03896a234bf 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 f93b98dd681..0db3dbdce05 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 @@ - # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories. - # Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling. - system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0] -- if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}: -+ if system_name in {"emscripten", "ios", "tvos", "visionos", "vxworks", "wasi", "watchos"}: - return None - - def joinuser(*args): -@@ -730,6 +733,18 @@ - release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") - osname = sys.platform - machine = sys.implementation._multiarch -+ elif sys.platform == "tvos": -+ release = get_config_vars().get("TVOS_DEPLOYMENT_TARGET", "9.0") -+ osname = sys.platform -+ machine = sys.implementation._multiarch -+ elif sys.platform == "watchos": -+ release = get_config_vars().get("WATCHOS_DEPLOYMENT_TARGET", "4.0") -+ osname = sys.platform -+ machine = sys.implementation._multiarch -+ elif sys.platform == "visionos": -+ release = get_config_vars().get("XROS_DEPLOYMENT_TARGET", "2.0") -+ osname = sys.platform -+ machine = sys.implementation._multiarch - else: - import _osx_support - osname, release, machine = _osx_support.get_platform_osx( -diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py -index 1c1cbd03d02..1378985de4a 100644 ---- a/Lib/test/datetimetester.py -+++ b/Lib/test/datetimetester.py -@@ -7159,9 +7159,9 @@ - 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 001ecec4dcd..e46a5661fe8 100644 ---- a/Lib/test/support/__init__.py -+++ b/Lib/test/support/__init__.py -@@ -559,7 +559,7 @@ - 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 -@@ -575,7 +575,7 @@ - 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 15603dc3d77..bff6c0fb95f 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 719c4feace6..92a831a9148 100644 ---- a/Lib/test/test_platform.py -+++ b/Lib/test/test_platform.py -@@ -271,13 +271,21 @@ - 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 4c3ea1cd8df..04a210e5c86 100644 ---- a/Lib/test/test_webbrowser.py -+++ b/Lib/test/test_webbrowser.py -@@ -236,7 +236,8 @@ - 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 f2e2394089d..2efbbfb0014 100644 ---- a/Lib/webbrowser.py -+++ b/Lib/webbrowser.py -@@ -488,7 +488,8 @@ - # 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 @@ - 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 01e10d1ab20..ddc430beb80 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -209,6 +209,12 @@ - # 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 ++def configure_build_python(context: argparse.Namespace) -> None: ++ """The implementation of the "configure-build" command.""" ++ if context.clean: ++ clean(context, "build") + ++ with ( ++ group("Configuring build Python"), ++ cwd(subdir("build", create=True)), ++ ): ++ command = [relpath(PYTHON_DIR / "configure")] ++ if context.args: ++ command.extend(context.args) ++ run(command) + - # Option to install to strip binaries - STRIPFLAG=-s - -@@ -2294,7 +2300,7 @@ - # 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 \ -@@ -2314,11 +2320,41 @@ - 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 f5cd73bdea8..6c1863c943b 100644 ---- a/Misc/platform_triplet.c -+++ b/Misc/platform_triplet.c -@@ -257,6 +257,32 @@ - # else - PLATFORM_TRIPLET=arm64-iphoneos - # endif -+# elif defined(TARGET_OS_TV) && TARGET_OS_TV -+# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR -+# if __x86_64__ -+PLATFORM_TRIPLET=x86_64-appletvsimulator -+# else -+PLATFORM_TRIPLET=arm64-appletvsimulator -+# endif -+# else -+PLATFORM_TRIPLET=arm64-appletvos -+# endif -+# elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH -+# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR -+# if __x86_64__ -+PLATFORM_TRIPLET=x86_64-watchsimulator -+# else -+PLATFORM_TRIPLET=arm64-watchsimulator -+# endif -+# else -+PLATFORM_TRIPLET=arm64_32-watchos -+# endif -+# elif defined(TARGET_OS_VISION) && TARGET_OS_VISION -+# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR -+PLATFORM_TRIPLET=arm64-xrsimulator -+# else -+PLATFORM_TRIPLET=arm64-xros -+# endif - // Older macOS SDKs do not define TARGET_OS_OSX - # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX - PLATFORM_TRIPLET=darwin -diff --git a/config.sub b/config.sub -index 1bb6a05dc11..49febd56a37 100755 ---- a/config.sub -+++ b/config.sub -@@ -1743,7 +1743,7 @@ - | 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 @@ - ;; - *-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 345822a6c6d..11acfe0ac9f 100755 ---- a/configure -+++ b/configure -@@ -982,6 +982,10 @@ - CFLAGS - CC - HAS_XCRUN -+EXPORT_XROS_DEPLOYMENT_TARGET -+XROS_DEPLOYMENT_TARGET -+WATCHOS_DEPLOYMENT_TARGET -+TVOS_DEPLOYMENT_TARGET - IPHONEOS_DEPLOYMENT_TARGET - EXPORT_MACOSX_DEPLOYMENT_TARGET - CONFIGURE_MACOSX_DEPLOYMENT_TARGET -@@ -4116,6 +4120,15 @@ - *-apple-ios*) - ac_sys_system=iOS - ;; -+ *-apple-tvos*) -+ ac_sys_system=tvOS -+ ;; -+ *-apple-watchos*) -+ ac_sys_system=watchOS -+ ;; -+ *-apple-xros*) -+ ac_sys_system=visionOS -+ ;; - *-*-darwin*) - ac_sys_system=Darwin - ;; -@@ -4197,7 +4210,7 @@ - # On cross-compile builds, configure will look for a host-specific compiler by - # prepending the user-provided host triple to the required binary name. - # --# On iOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", -+# On iOS/tvOS/watchOS/visionOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", - # which isn't a binary that exists, and isn't very convenient, as it contains the - # iOS version. As the default cross-compiler name won't exist, configure falls - # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -4212,6 +4225,17 @@ - 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 ;; ++def make_build_python(context: argparse.Namespace) -> None: ++ """The implementation of the "make-build" command.""" ++ with ( ++ group("Compiling build Python"), ++ cwd(subdir("build")), ++ ): ++ run(["make", "-j", str(os.cpu_count())]) + -+ aarch64-apple-watchos*-simulator) AR=arm64-apple-watchos-simulator-ar ;; -+ aarch64-apple-watchos*) AR=arm64_32-apple-watchos-ar ;; -+ x86_64-apple-watchos*-simulator) AR=x86_64-apple-watchos-simulator-ar ;; + -+ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; -+ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;; - *) - esac - fi -@@ -4220,6 +4244,17 @@ - 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 ;; ++def apple_target(host: str) -> str: ++ """Return the Apple platform identifier for a given host triple.""" ++ for _, platform_slices in HOSTS.items(): ++ for slice_name, slice_parts in platform_slices.items(): ++ for host_triple, multiarch in slice_parts.items(): ++ if host == host_triple: ++ return ".".join(multiarch.split("-")[::-1]) + -+ 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 ;; ++ raise KeyError(host) + -+ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;; -+ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;; -+ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;; + -+ aarch64-apple-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;; -+ aarch64-apple-xros*) CC=arm64-apple-xros-clang ;; - *) - esac - fi -@@ -4228,6 +4263,17 @@ - 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 ;; ++def apple_multiarch(host: str) -> str: ++ """Return the multiarch descriptor for a given host triple.""" ++ for _, platform_slices in HOSTS.items(): ++ for slice_name, slice_parts in platform_slices.items(): ++ for host_triple, multiarch in slice_parts.items(): ++ if host == host_triple: ++ return multiarch + -+ 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 ;; ++ raise KeyError(host) + -+ aarch64-apple-watchos*-simulator) CPP=arm64-apple-watchos-simulator-cpp ;; -+ aarch64-apple-watchos*) CPP=arm64_32-apple-watchos-cpp ;; -+ x86_64-apple-watchos*-simulator) CPP=x86_64-apple-watchos-simulator-cpp ;; + -+ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; -+ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;; - *) - esac - fi -@@ -4236,6 +4282,17 @@ - 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++ ;; ++def unpack_deps( ++ platform: str, ++ host: str, ++ prefix_dir: Path, ++ cache_dir: Path, ++) -> None: ++ """Unpack binary dependencies into a provided directory. + -+ 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++ ;; ++ Downloads binaries if they aren't already present. Downloads will be stored ++ in provided cache directory. + -+ 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++ ;; ++ On non-macOS platforms, as a safety mechanism, any dynamic libraries will be ++ purged from the unpacked dependencies. ++ """ ++ deps_url = "https://github.com/beeware/cpython-apple-source-deps/releases/download" ++ for name_ver in [ ++ "BZip2-1.0.8-2", ++ "libFFI-3.4.7-2", ++ "OpenSSL-3.0.17-1", ++ "XZ-5.6.4-2", ++ "mpdecimal-4.0.0-2", ++ "zstd-1.5.7-1", ++ ]: ++ filename = f"{name_ver.lower()}-{apple_target(host)}.tar.gz" ++ archive_path = download( ++ f"{deps_url}/{name_ver}/{filename}", ++ target_dir=cache_dir, ++ ) ++ shutil.unpack_archive(archive_path, prefix_dir) + -+ aarch64-apple-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;; -+ aarch64-apple-xros*) CXX=arm64-apple-xros-clang++ ;; - *) - esac - fi -@@ -4358,8 +4415,11 @@ - case $enableval in - yes) - case $ac_sys_system in -- Darwin) enableval=/Library/Frameworks ;; -- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; -+ Darwin) enableval=/Library/Frameworks ;; -+ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; -+ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;; -+ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;; -+ visionOS) enableval=visionOS/Frameworks/\$\(MULTIARCH\) ;; - *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 - esac - esac -@@ -4368,6 +4428,9 @@ - no) - case $ac_sys_system in - iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; -+ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; -+ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; -+ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;; - *) - PYTHONFRAMEWORK= - PYTHONFRAMEWORKDIR=no-framework -@@ -4474,6 +4537,51 @@ - - ac_config_files="$ac_config_files iOS/Resources/Info.plist" - -+ ;; -+ tvOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ # Dynamic libraries will be preferentially linked over static; On non-macOS ++ # platforms, ensure that no dylibs are available in the prefix folder. ++ if platform != "macOS": ++ for dylib in prefix_dir.glob("**/*.dylib"): ++ dylib.unlink() + -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=tvOS/Resources + -+ ac_config_files="$ac_config_files tvOS/Resources/Info.plist" ++def download(url: str, target_dir: Path) -> Path: ++ """Download the specified URL into the given directory. + -+ ;; -+ watchOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ :return: The path to the downloaded archive. ++ """ ++ target_path = Path(target_dir).resolve() ++ target_path.mkdir(exist_ok=True, parents=True) ++ ++ out_path = target_path / basename(url) ++ if not Path(out_path).is_file(): ++ run( ++ [ ++ "curl", ++ "-Lf", ++ "--retry", ++ "5", ++ "--retry-all-errors", ++ "-o", ++ out_path, ++ url, ++ ] ++ ) ++ else: ++ print(f"Using cached version of {basename(url)}") ++ return out_path + -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=watchOS/Resources + -+ ac_config_files="$ac_config_files watchOS/Resources/Info.plist" ++def configure_host_python( ++ context: argparse.Namespace, ++ host: str | None = None, ++) -> None: ++ """The implementation of the "configure-host" command.""" ++ if host is None: ++ host = context.host + -+ ;; -+ visionOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ if context.clean: ++ clean(context, host) + -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=visionOS/Resources ++ host_dir = subdir(host, create=True) ++ prefix_dir = host_dir / "prefix" + -+ ac_config_files="$ac_config_files visionOS/Resources/Info.plist" ++ with group(f"Downloading dependencies ({host})"): ++ if not prefix_dir.exists(): ++ prefix_dir.mkdir() ++ unpack_deps(context.platform, host, prefix_dir, context.cache_dir) ++ else: ++ print("Dependencies already installed") ++ ++ with ( ++ group(f"Configuring host Python ({host})"), ++ cwd(host_dir), ++ ): ++ command = [ ++ # Basic cross-compiling configuration ++ relpath(PYTHON_DIR / "configure"), ++ f"--host={host}", ++ f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}", ++ f"--with-build-python={build_python_path()}", ++ "--with-system-libmpdec", ++ "--enable-ipv6", ++ "--enable-framework", ++ # Dependent libraries. ++ f"--with-openssl={prefix_dir}", ++ f"LIBLZMA_CFLAGS=-I{prefix_dir}/include", ++ f"LIBLZMA_LIBS=-L{prefix_dir}/lib -llzma", ++ f"LIBFFI_CFLAGS=-I{prefix_dir}/include", ++ f"LIBFFI_LIBS=-L{prefix_dir}/lib -lffi", ++ f"LIBMPDEC_CFLAGS=-I{prefix_dir}/include", ++ f"LIBMPDEC_LIBS=-L{prefix_dir}/lib -lmpdec", ++ f"LIBZSTD_CFLAGS=-I{prefix_dir}/include", ++ f"LIBZSTD_LIBS=-L{prefix_dir}/lib -lzstd", ++ ] + - ;; - *) - as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 -@@ -4485,6 +4593,9 @@ - e) - case $ac_sys_system in - iOS) as_fn_error $? "iOS builds must use --enable-framework" "$LINENO" 5 ;; -+ tvOS) as_fn_error $? "tvOS builds must use --enable-framework" "$LINENO" 5 ;; -+ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; -+ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;; - *) - PYTHONFRAMEWORK= - PYTHONFRAMEWORKDIR=no-framework -@@ -4539,8 +4650,8 @@ - case "$withval" in - yes) - case $ac_sys_system in -- Darwin|iOS) -- # iOS is able to share the macOS patch -+ Darwin|iOS|tvOS|watchOS|visionOS) -+ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch - APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" - ;; - *) as_fn_error $? "no default app store compliance patch available for $ac_sys_system" "$LINENO" 5 ;; -@@ -4558,8 +4669,8 @@ - else case e in #( - e) - case $ac_sys_system in -- iOS) -- # Always apply the compliance patch on iOS; we can use the macOS patch -+ iOS|tvOS|watchOS|visionOS) -+ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch - APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: applying default app store compliance patch" >&5 - printf "%s\n" "applying default app store compliance patch" >&6; } -@@ -4577,6 +4688,8 @@ - - - -+EXPORT_XROS_DEPLOYMENT_TARGET='#' ++ if context.args: ++ command.extend(context.args) ++ run(command, host=host) + - - if test "$cross_compiling" = yes; then - case "$host" in -@@ -4614,6 +4727,78 @@ - ;; - 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; } ++def make_host_python( ++ context: argparse.Namespace, ++ host: str | None = None, ++) -> None: ++ """The implementation of the "make-host" command.""" ++ if host is None: ++ host = context.host + -+ 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} ++ with ( ++ group(f"Compiling host Python ({host})"), ++ cwd(subdir(host)), ++ ): ++ run(["make", "-j", str(os.cpu_count())], host=host) ++ run(["make", "install"], host=host) + -+ # WATCHOS_DEPLOYMENT_TARGET is the minimum supported watchOS version -+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking watchOS deployment target" >&5 -+printf %s "checking watchOS deployment target... " >&6; } -+ WATCHOS_DEPLOYMENT_TARGET=${_host_os:7} -+ WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:=4.0} -+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $WATCHOS_DEPLOYMENT_TARGET" >&5 -+printf "%s\n" "$WATCHOS_DEPLOYMENT_TARGET" >&6; } + -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device} -+ ;; -+ *) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} -+ ;; -+ esac -+ ;; -+ *-apple-xros*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ _host_device=`echo $host | cut -d '-' -f4` -+ _host_device=${_host_device:=os} ++def framework_path(host_triple: str, multiarch: str) -> Path: ++ """The path to a built single-architecture framework product. + -+ # 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; } ++ :param host_triple: The host triple (e.g., arm64-apple-ios-simulator) ++ :param multiarch: The multiarch identifier (e.g., arm64-simulator) ++ """ ++ return ( ++ CROSS_BUILD_DIR ++ / f"{host_triple}/Apple/{platform_for_host(host_triple)}/Frameworks/{multiarch}" ++ ) + -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device} -+ ;; -+ *) -+ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device} -+ ;; -+ esac -+ ;; - *-*-darwin*) - case "$host_cpu" in - arm*) -@@ -4704,9 +4889,15 @@ - define_xopen_source=no;; - Darwin/[12][0-9].*) - define_xopen_source=no;; -- # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. -+ # On iOS/tvOS/watchOS/visionOS, defining _POSIX_C_SOURCE also disables platform specific features. - iOS/*) - define_xopen_source=no;; -+ tvOS/*) -+ define_xopen_source=no;; -+ watchOS/*) -+ define_xopen_source=no;; -+ visionOS/*) -+ define_xopen_source=no;; - # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from - # defining NI_NUMERICHOST. - QNX/6.3.2) -@@ -4769,7 +4960,13 @@ - CONFIGURE_MACOSX_DEPLOYMENT_TARGET= - EXPORT_MACOSX_DEPLOYMENT_TARGET='#' - --# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. -+# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / -+# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. + ++def package_version(prefix_path: Path) -> str: ++ """Extract the Python version being built from patchlevel.h.""" ++ for path in prefix_path.glob("**/patchlevel.h"): ++ text = path.read_text(encoding="utf-8") ++ if match := re.search( ++ r'\n\s*#define\s+PY_VERSION\s+"(.+)"\s*\n', text ++ ): ++ version = match[1] ++ # If not building against a tagged commit, add a timestamp to the ++ # version. Follow the PyPA version number rules, as this will make ++ # it easier to process with other tools. The version will have a ++ # `+` suffix once any official release has been made; a freshly ++ # forked main branch will have a version of 3.X.0a0. ++ if version.endswith("a0"): ++ version += "+" ++ if version.endswith("+"): ++ version += datetime.now(timezone.utc).strftime("%Y%m%d.%H%M%S") ++ ++ return version ++ ++ sys.exit("Unable to determine Python version being packaged.") ++ ++ ++def lib_platform_files(dirname, names): ++ """A file filter that ignores platform-specific files in the lib directory.""" ++ path = Path(dirname) ++ if ( ++ path.parts[-3] == "lib" ++ and path.parts[-2].startswith("python") ++ and path.parts[-1] == "lib-dynload" ++ ): ++ return names ++ elif path.parts[-2] == "lib" and path.parts[-1].startswith("python"): ++ ignored_names = set( ++ name ++ for name in names ++ if ( ++ name.startswith("_sysconfigdata_") ++ or name.startswith("_sysconfig_vars_") ++ or name == "build-details.json" ++ ) ++ ) ++ else: ++ ignored_names = set() + ++ return ignored_names + + -+# XROS_DEPLOYMENT_TARGET should get exported - - - # checks for alternative programs -@@ -4810,6 +5007,16 @@ - as_fn_append CFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" - as_fn_append LDFLAGS " -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}" - ;; #( -+ tvOS) : ++def lib_non_platform_files(dirname, names): ++ """A file filter that ignores anything *except* platform-specific files ++ in the lib directory. ++ """ ++ path = Path(dirname) ++ if path.parts[-2] == "lib" and path.parts[-1].startswith("python"): ++ return ( ++ set(names) - lib_platform_files(dirname, names) - {"lib-dynload"} ++ ) ++ else: ++ return set() + -+ as_fn_append CFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" -+ as_fn_append LDFLAGS " -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}" -+ ;; #( -+ watchOS) : + -+ as_fn_append CFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" -+ as_fn_append LDFLAGS " -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}" -+ ;; #( - *) : - ;; - esac -@@ -7179,6 +7386,12 @@ - MULTIARCH="" ;; #( - iOS) : - MULTIARCH="" ;; #( -+ tvOS) : -+ MULTIARCH="" ;; #( -+ watchOS) : -+ MULTIARCH="" ;; #( -+ visionOS) : -+ MULTIARCH="" ;; #( - FreeBSD*) : - MULTIARCH="" ;; #( - *) : -@@ -7199,7 +7412,7 @@ - printf "%s\n" "$MULTIARCH" >&6; } - - case $ac_sys_system in #( -- iOS) : -+ iOS|tvOS|watchOS|visionOS) : - SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( - *) : - SOABI_PLATFORM=$PLATFORM_TRIPLET -@@ -7250,6 +7463,18 @@ - PY_SUPPORT_TIER=3 ;; #( - aarch64-apple-ios*/clang) : - PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-tvos*-simulator/clang) : -+ PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-tvos*/clang) : -+ PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-watchos*-simulator/clang) : -+ PY_SUPPORT_TIER=3 ;; #( -+ arm64_32-apple-watchos*/clang) : -+ PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-xros*-simulator/clang) : -+ PY_SUPPORT_TIER=3 ;; #( -+ aarch64-apple-xros*/clang) : -+ PY_SUPPORT_TIER=3 ;; #( - aarch64-*-linux-android/clang) : - PY_SUPPORT_TIER=3 ;; #( - x86_64-*-linux-android/clang) : -@@ -7688,7 +7913,7 @@ - case $ac_sys_system in - Darwin) - LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; -- iOS) -+ iOS|tvOS|watchOS|visionOS) - LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; - *) - as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; -@@ -7754,7 +7979,7 @@ - BLDLIBRARY='-L. -lpython$(LDVERSION)' - RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} - ;; -- iOS) -+ iOS|tvOS|watchOS|visionOS) - LDLIBRARY='libpython$(LDVERSION).dylib' - ;; - AIX*) -@@ -13570,7 +13795,7 @@ - BLDSHARED="$LDSHARED" - fi - ;; -- iOS/*) -+ iOS/*|tvOS/*|watchOS/*|visionOS/*) - LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' - LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' - BLDSHARED="$LDSHARED" -@@ -13703,7 +13928,7 @@ - Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; - Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; - # -u libsys_s pulls in all symbols in libsys -- Darwin/*|iOS/*) -+ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) - LINKFORSHARED="$extra_undefs -framework CoreFoundation" - - # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -13727,7 +13952,7 @@ - LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - fi - LINKFORSHARED="$LINKFORSHARED" -- elif test $ac_sys_system = "iOS"; then -+ elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS"; then - LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' - fi - ;; -@@ -15504,7 +15729,7 @@ - - ctypes_malloc_closure=yes - ;; #( -- iOS) : -+ iOS|tvOS|watchOS|visionOS) : - - ctypes_malloc_closure=yes - ;; #( -@@ -19256,12 +19481,6 @@ - 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 -@@ -19322,18 +19541,6 @@ - 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 -@@ -19766,24 +19973,6 @@ - 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 -@@ -20102,12 +20291,6 @@ - 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 -@@ -20376,11 +20559,11 @@ - - fi - --# iOS defines some system methods that can be linked (so they are -+# iOS/tvOS/watchOS/visionOS define some system methods that can be linked (so they are - # found by configure), but either raise a compilation error (because the - # header definition prevents usage - autoconf doesn't use the headers), or - # raise an error if used at runtime. Force these symbols off. --if test "$ac_sys_system" != "iOS" ; then -+if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then - ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" - if test "x$ac_cv_func_getentropy" = xyes - then : -@@ -20402,6 +20585,53 @@ - - 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 ++def create_xcframework(platform: str) -> str: ++ """Build an XCframework from the component parts for the platform. + -+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 ++ :return: The version number of the Python verion that was packaged. ++ """ ++ package_path = CROSS_BUILD_DIR / platform ++ try: ++ package_path.mkdir() ++ except FileExistsError: ++ raise RuntimeError( ++ f"{platform} XCframework already exists; do you need to run with --clean?" ++ ) from None ++ ++ frameworks = [] ++ # Merge Frameworks for each component SDK. If there's only one architecture ++ # for the SDK, we can use the compiled Python.framework as-is. However, if ++ # there's more than architecture, we need to merge the individual built ++ # frameworks into a merged "fat" framework. ++ for slice_name, slice_parts in HOSTS[platform].items(): ++ # Some parts are the same across all slices, so we use can any of the ++ # host frameworks as the source for the merged version. Use the first ++ # one on the list, as it's as representative as any other. ++ first_host_triple, first_multiarch = next(iter(slice_parts.items())) ++ first_framework = ( ++ framework_path(first_host_triple, first_multiarch) ++ / "Python.framework" ++ ) + -+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 ++ if len(slice_parts) == 1: ++ # The first framework is the only framework, so copy it. ++ print(f"Copying framework for {slice_name}...") ++ frameworks.append(first_framework) ++ else: ++ print(f"Merging framework for {slice_name}...") ++ slice_path = CROSS_BUILD_DIR / slice_name ++ slice_framework = slice_path / "Python.framework" ++ slice_framework.mkdir(exist_ok=True, parents=True) ++ ++ # Copy the Info.plist ++ shutil.copy( ++ first_framework / "Info.plist", ++ slice_framework / "Info.plist", ++ ) + -+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 ++ # Copy the headers ++ shutil.copytree( ++ first_framework / "Headers", ++ slice_framework / "Headers", ++ ) + -+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 ++ # Create the "fat" library binary for the slice ++ run( ++ ["lipo", "-create", "-output", slice_framework / "Python"] ++ + [ ++ ( ++ framework_path(host_triple, multiarch) ++ / "Python.framework/Python" ++ ) ++ for host_triple, multiarch in slice_parts.items() ++ ] ++ ) + -+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 ++ # Add this merged slice to the list to be added to the XCframework ++ frameworks.append(slice_framework) + -+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 ++ print() ++ print("Build XCframework...") ++ cmd = [ ++ "xcodebuild", ++ "-create-xcframework", ++ "-output", ++ package_path / "Python.xcframework", ++ ] ++ for framework in frameworks: ++ cmd.extend(["-framework", framework]) ++ ++ run(cmd) ++ ++ # Extract the package version from the merged framework ++ version = package_version(package_path / "Python.xcframework") ++ version_tag = ".".join(version.split(".")[:2]) ++ ++ # On non-macOS platforms, each framework in XCframework only contains the ++ # headers, libPython, plus an Info.plist. Other resources like the standard ++ # library and binary shims aren't allowed to live in framework; they need ++ # to be copied in separately. ++ print() ++ print("Copy additional resources...") ++ has_common_stdlib = False ++ for slice_name, slice_parts in HOSTS[platform].items(): ++ # Some parts are the same across all slices, so we can any of the ++ # host frameworks as the source for the merged version. ++ first_host_triple, first_multiarch = next(iter(slice_parts.items())) ++ first_path = framework_path(first_host_triple, first_multiarch) ++ first_framework = first_path / "Python.framework" ++ ++ slice_path = package_path / f"Python.xcframework/{slice_name}" ++ slice_framework = slice_path / "Python.framework" ++ ++ # Copy the binary helpers ++ print(f" - {slice_name} binaries") ++ shutil.copytree(first_path / "bin", slice_path / "bin") ++ ++ # Copy the include path (this will be a symlink to the framework headers) ++ print(f" - {slice_name} include files") ++ shutil.copytree( ++ first_path / "include", ++ slice_path / "include", ++ symlinks=True, ++ ) + -+fi ++ # Copy in the cross-architecture pyconfig.h ++ shutil.copy( ++ PYTHON_DIR / f"Apple/{platform}/Resources/pyconfig.h", ++ slice_framework / "Headers/pyconfig.h", ++ ) + -+fi ++ print(f" - {slice_name} architecture-specific files") ++ for host_triple, multiarch in slice_parts.items(): ++ print(f" - {multiarch} standard library") ++ arch, _ = multiarch.split("-", 1) ++ ++ if not has_common_stdlib: ++ print(" - using this architecture as the common stdlib") ++ shutil.copytree( ++ framework_path(host_triple, multiarch) / "lib", ++ package_path / "Python.xcframework/lib", ++ ignore=lib_platform_files, ++ ) ++ has_common_stdlib = True + - { 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} -@@ -23873,7 +24103,8 @@ - - - # 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 : -@@ -23987,7 +24218,7 @@ - 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 : -@@ -24170,6 +24401,7 @@ - fi - - done -+fi - - # check for long file support functions - ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" -@@ -24435,10 +24667,10 @@ - - done - --# On Android and iOS, clock_settime can be linked (so it is found by -+# On Android, iOS, tvOS, watchOS, and visionOS, clock_settime can be linked (so it is found by - # configure), but when used in an unprivileged process, it crashes rather than - # returning an error. Force the symbol off. --if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" -+if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" - then - - for ac_func in clock_settime -@@ -24755,7 +24987,7 @@ - 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" -@@ -26777,8 +27009,8 @@ - LIBPYTHON="\$(BLDLIBRARY)" - fi - --# On iOS the shared libraries must be linked with the Python framework --if test "$ac_sys_system" = "iOS"; then -+# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework -+if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then - MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" - fi - -@@ -29648,7 +29880,7 @@ - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for device files" >&5 - printf "%s\n" "$as_me: checking for device files" >&6;} - --if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then -+if test "$ac_sys_system" = "Linux-android" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS" ; then - ac_cv_file__dev_ptmx=no - ac_cv_file__dev_ptc=no - else -@@ -30155,7 +30387,7 @@ - with_ensurepip=no ;; #( - WASI) : - with_ensurepip=no ;; #( -- iOS) : -+ iOS|tvOS|watchOS|visionOS) : - with_ensurepip=no ;; #( - *) : - with_ensurepip=upgrade -@@ -31104,7 +31336,7 @@ - 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=;; -@@ -31136,7 +31368,7 @@ - ;; #( - Darwin) : - ;; #( -- iOS) : -+ iOS|tvOS|watchOS|visionOS) : - - - -@@ -35304,6 +35536,9 @@ - "Mac/Resources/framework/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/framework/Info.plist" ;; - "Mac/Resources/app/Info.plist") CONFIG_FILES="$CONFIG_FILES Mac/Resources/app/Info.plist" ;; - "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; -+ "tvOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES tvOS/Resources/Info.plist" ;; -+ "watchOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES watchOS/Resources/Info.plist" ;; -+ "visionOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES visionOS/Resources/Info.plist" ;; - "Makefile.pre") CONFIG_FILES="$CONFIG_FILES Makefile.pre" ;; - "Misc/python.pc") CONFIG_FILES="$CONFIG_FILES Misc/python.pc" ;; - "Misc/python-embed.pc") CONFIG_FILES="$CONFIG_FILES Misc/python-embed.pc" ;; -diff --git a/configure.ac b/configure.ac -index d6059471771..7e1a5ffe604 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -330,6 +330,15 @@ - *-apple-ios*) - ac_sys_system=iOS - ;; -+ *-apple-tvos*) -+ ac_sys_system=tvOS -+ ;; -+ *-apple-watchos*) -+ ac_sys_system=watchOS -+ ;; -+ *-apple-xros*) -+ ac_sys_system=visionOS -+ ;; - *-*-darwin*) - ac_sys_system=Darwin - ;; -@@ -405,7 +414,7 @@ - # On cross-compile builds, configure will look for a host-specific compiler by - # prepending the user-provided host triple to the required binary name. - # --# On iOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", -+# On iOS/tvOS/watchOS/visionOS, this results in binaries like "arm64-apple-ios13.0-simulator-gcc", - # which isn't a binary that exists, and isn't very convenient, as it contains the - # iOS version. As the default cross-compiler name won't exist, configure falls - # back to gcc, which *definitely* won't work. We're providing wrapper scripts for -@@ -420,6 +429,17 @@ - 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 ;; ++ shutil.copytree( ++ framework_path(host_triple, multiarch) / "lib", ++ slice_path / f"lib-{arch}", ++ ignore=lib_non_platform_files, ++ ) + -+ 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 ;; ++ # Copy the host's pyconfig.h to an architecture-specific name. ++ arch = multiarch.split("-")[0] ++ host_path = ( ++ CROSS_BUILD_DIR ++ / host_triple ++ / f"Apple/{platform}/Frameworks" ++ / multiarch ++ ) ++ host_framework = host_path / "Python.framework" ++ shutil.copy( ++ host_framework / "Headers/pyconfig.h", ++ slice_framework / f"Headers/pyconfig-{arch}.h", ++ ) + -+ 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 ;; ++ # Apple identifies certain libraries as "security risks"; if you ++ # statically link those libraries into a Framework, you become ++ # responsible for providing a privacy manifest for that framework. ++ xcprivacy_file = { ++ "OpenSSL": subdir(host_triple) ++ / "prefix/share/OpenSSL.xcprivacy" ++ } ++ print(f" - {multiarch} xcprivacy files") ++ for module, lib in [ ++ ("_hashlib", "OpenSSL"), ++ ("_ssl", "OpenSSL"), ++ ]: ++ shutil.copy( ++ xcprivacy_file[lib], ++ slice_path ++ / f"lib-{arch}/python{version_tag}/lib-dynload/{module}.xcprivacy", ++ ) + -+ aarch64-apple-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; -+ aarch64-apple-xros*) AR=arm64-apple-xros-ar ;; - *) - esac - fi -@@ -428,6 +448,17 @@ - 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 ;; ++ print(" - build tools") ++ shutil.copytree( ++ PYTHON_DIR / "Apple/testbed/Python.xcframework/build", ++ package_path / "Python.xcframework/build", ++ ) + -+ 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 ;; ++ return version + -+ aarch64-apple-watchos*-simulator) CC=arm64-apple-watchos-simulator-clang ;; -+ aarch64-apple-watchos*) CC=arm64_32-apple-watchos-clang ;; -+ x86_64-apple-watchos*-simulator) CC=x86_64-apple-watchos-simulator-clang ;; + -+ aarch64-apple-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;; -+ aarch64-apple-xros*) CC=arm64-apple-xros-clang ;; - *) - esac - fi -@@ -436,6 +467,17 @@ - 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 ;; ++def package(context: argparse.Namespace) -> None: ++ """The implementation of the "package" command.""" ++ if context.clean: ++ clean(context, "package") + -+ 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 ;; ++ with group("Building package"): ++ # Create an XCframework ++ version = create_xcframework(context.platform) + -+ 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 ;; ++ # watchOS doesn't have a testbed (yet!) ++ if context.platform != "watchOS": ++ # Clone testbed ++ print() ++ run( ++ [ ++ sys.executable, ++ "Apple/testbed", ++ "clone", ++ "--platform", ++ context.platform, ++ "--framework", ++ CROSS_BUILD_DIR / context.platform / "Python.xcframework", ++ CROSS_BUILD_DIR / context.platform / "testbed", ++ ] ++ ) + -+ aarch64-apple-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; -+ aarch64-apple-xros*) CPP=arm64-apple-xros-cpp ;; - *) - esac - fi -@@ -444,6 +486,17 @@ - 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++ ;; ++ # Build the final archive ++ archive_name = ( ++ CROSS_BUILD_DIR ++ / "dist" ++ / f"python-{version}-{context.platform}-XCframework" ++ ) + -+ 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++ ;; ++ print() ++ print("Create package archive...") ++ shutil.make_archive( ++ str(CROSS_BUILD_DIR / archive_name), ++ format="gztar", ++ root_dir=CROSS_BUILD_DIR / context.platform, ++ base_dir=".", ++ ) ++ print() ++ print(f"{archive_name.relative_to(PYTHON_DIR)}.tar.gz created.") + -+ aarch64-apple-watchos*-simulator) CXX=arm64-apple-watchos-simulator-clang++ ;; -+ aarch64-apple-watchos*) CXX=arm64_32-apple-watchos-clang++ ;; -+ x86_64-apple-watchos*-simulator) CXX=x86_64-apple-watchos-simulator-clang++ ;; + -+ aarch64-apple-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;; -+ aarch64-apple-xros*) CXX=arm64-apple-xros-clang++ ;; - *) - esac - fi -@@ -558,8 +611,11 @@ - case $enableval in - yes) - case $ac_sys_system in -- Darwin) enableval=/Library/Frameworks ;; -- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; -+ Darwin) enableval=/Library/Frameworks ;; -+ iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; -+ tvOS) enableval=tvOS/Frameworks/\$\(MULTIARCH\) ;; -+ watchOS) enableval=watchOS/Frameworks/\$\(MULTIARCH\) ;; -+ visionOS) enableval=visionOS/Frameworks/\$\(MULTIARCH\) ;; - *) AC_MSG_ERROR([Unknown platform for framework build]) - esac - esac -@@ -568,6 +624,9 @@ - no) - case $ac_sys_system in - iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; -+ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;; -+ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; -+ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;; - *) - PYTHONFRAMEWORK= - PYTHONFRAMEWORKDIR=no-framework -@@ -670,6 +729,48 @@ - - AC_CONFIG_FILES([iOS/Resources/Info.plist]) - ;; -+ tvOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++def build(context: argparse.Namespace, host: str | None = None) -> None: ++ """The implementation of the "build" command.""" ++ if host is None: ++ host = context.host + -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=tvOS/Resources ++ if context.clean: ++ clean(context, host) + -+ AC_CONFIG_FILES([tvOS/Resources/Info.plist]) -+ ;; -+ watchOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ if host in {"all", "build"}: ++ for step in [ ++ configure_build_python, ++ make_build_python, ++ ]: ++ step(context) + -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=watchOS/Resources ++ if host == "build": ++ hosts = [] ++ elif host in {"all", "hosts"}: ++ hosts = all_host_triples(context.platform) ++ else: ++ hosts = [host] + -+ AC_CONFIG_FILES([watchOS/Resources/Info.plist]) -+ ;; -+ visionOS) : -+ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" -+ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " -+ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" -+ FRAMEWORKPYTHONW= -+ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ for step_host in hosts: ++ for step in [ ++ configure_host_python, ++ make_host_python, ++ ]: ++ step(context, host=step_host) + -+ prefix=$PYTHONFRAMEWORKPREFIX -+ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" -+ RESSRCDIR=visionOS/Resources ++ if host in {"all", "hosts"}: ++ package(context) + -+ AC_CONFIG_FILES([visionOS/Resources/Info.plist]) -+ ;; - *) - AC_MSG_ERROR([Unknown platform for framework build]) - ;; -@@ -678,6 +779,9 @@ - ],[ - case $ac_sys_system in - iOS) AC_MSG_ERROR([iOS builds must use --enable-framework]) ;; -+ tvOS) AC_MSG_ERROR([tvOS builds must use --enable-framework]) ;; -+ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; -+ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;; - *) - PYTHONFRAMEWORK= - PYTHONFRAMEWORKDIR=no-framework -@@ -730,8 +834,8 @@ - case "$withval" in - yes) - case $ac_sys_system in -- Darwin|iOS) -- # iOS is able to share the macOS patch -+ Darwin|iOS|tvOS|watchOS|visionOS) -+ # iOS/tvOS/watchOS/visionOS is able to share the macOS patch - APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" - ;; - *) AC_MSG_ERROR([no default app store compliance patch available for $ac_sys_system]) ;; -@@ -745,8 +849,8 @@ - esac - ],[ - case $ac_sys_system in -- iOS) -- # Always apply the compliance patch on iOS; we can use the macOS patch -+ iOS|tvOS|watchOS|visionOS) -+ # Always apply the compliance patch on iOS/tvOS/watchOS/visionOS; we can use the macOS patch - APP_STORE_COMPLIANCE_PATCH="Mac/Resources/app-store-compliance.patch" - AC_MSG_RESULT([applying default app store compliance patch]) - ;; -@@ -759,6 +863,8 @@ - ]) - AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) - -+EXPORT_XROS_DEPLOYMENT_TARGET='#' + - AC_SUBST([_PYTHON_HOST_PLATFORM]) - if test "$cross_compiling" = yes; then - case "$host" in -@@ -794,6 +900,70 @@ - ;; - esac - ;; -+ *-apple-tvos*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ _host_device=`echo $host | cut -d '-' -f4` -+ _host_device=${_host_device:=os} ++def test(context: argparse.Namespace, host: str | None = None) -> None: ++ """The implementation of the "test" command.""" ++ if host is None: ++ host = context.host + -+ # 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]) ++ if context.clean: ++ clean(context, "test") + -+ 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} ++ with group(f"Test {'XCframework' if host in {'all', 'hosts'} else host}"): ++ timestamp = str(time.time_ns())[:-6] ++ testbed_dir = ( ++ CROSS_BUILD_DIR / f"{context.platform}-testbed.{timestamp}" ++ ) ++ if host in {"all", "hosts"}: ++ framework_path = ( ++ CROSS_BUILD_DIR / context.platform / "Python.xcframework" ++ ) ++ else: ++ build_arch = platform.machine() ++ host_arch = host.split("-")[0] + -+ # 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]) ++ if not host.endswith("-simulator"): ++ print("Skipping test suite non-simulator build.") ++ return ++ elif build_arch != host_arch: ++ print( ++ f"Skipping test suite for an {host_arch} build " ++ f"on an {build_arch} machine." ++ ) ++ return ++ else: ++ framework_path = ( ++ CROSS_BUILD_DIR ++ / host ++ / f"Apple/{context.platform}" ++ / f"Frameworks/{apple_multiarch(host)}" ++ ) + -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-arm64-watch${_host_device} -+ ;; -+ *) -+ _host_ident=${WATCHOS_DEPLOYMENT_TARGET}-$host_cpu-watch${_host_device} -+ ;; -+ esac -+ ;; -+ *-apple-xros*) -+ _host_os=`echo $host | cut -d '-' -f3` -+ _host_device=`echo $host | cut -d '-' -f4` -+ _host_device=${_host_device:=os} ++ run( ++ [ ++ sys.executable, ++ "Apple/testbed", ++ "clone", ++ "--platform", ++ context.platform, ++ "--framework", ++ framework_path, ++ testbed_dir, ++ ] ++ ) + -+ # 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]) ++ run( ++ [ ++ sys.executable, ++ testbed_dir, ++ "run", ++ "--verbose", ++ ] ++ + ( ++ ["--simulator", str(context.simulator)] ++ if context.simulator ++ else [] ++ ) ++ + [ ++ "--", ++ "test", ++ "--slow-ci" if context.slow else "--fast-ci", ++ "--single-process", ++ "--no-randomize", ++ # Timeout handling requires subprocesses; explicitly setting ++ # the timeout to -1 disables the faulthandler. ++ "--timeout=-1", ++ # Adding Python options requires the use of a subprocess to ++ # start a new Python interpreter. ++ "--dont-add-python-opts", ++ ] ++ ) + -+ case "$host_cpu" in -+ aarch64) -+ _host_ident=${XROS_DEPLOYMENT_TARGET}-arm64-xr${_host_device} -+ ;; -+ *) -+ _host_ident=${XROS_DEPLOYMENT_TARGET}-$host_cpu-xr${_host_device} -+ ;; -+ esac -+ ;; - *-*-darwin*) - case "$host_cpu" in - arm*) -@@ -883,9 +1053,15 @@ - define_xopen_source=no;; - Darwin/@<:@[12]@:>@@<:@0-9@:>@.*) - define_xopen_source=no;; -- # On iOS, defining _POSIX_C_SOURCE also disables platform specific features. -+ # On iOS/tvOS/watchOS/visionOS, defining _POSIX_C_SOURCE also disables platform specific features. - iOS/*) - define_xopen_source=no;; -+ tvOS/*) -+ define_xopen_source=no;; -+ watchOS/*) -+ define_xopen_source=no;; -+ visionOS/*) -+ define_xopen_source=no;; - # On QNX 6.3.2, defining _XOPEN_SOURCE prevents netdb.h from - # defining NI_NUMERICHOST. - QNX/6.3.2) -@@ -944,8 +1120,14 @@ - CONFIGURE_MACOSX_DEPLOYMENT_TARGET= - EXPORT_MACOSX_DEPLOYMENT_TARGET='#' - --# Record the value of IPHONEOS_DEPLOYMENT_TARGET enforced by the selected host triple. -+# Record the value of IPHONEOS_DEPLOYMENT_TARGET / TVOS_DEPLOYMENT_TARGET / -+# WATCHOS_DEPLOYMENT_TARGET / XROS_DEPLOYMENT_TARGET enforced by the selected host triple. - AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET]) -+AC_SUBST([TVOS_DEPLOYMENT_TARGET]) -+AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) -+AC_SUBST([XROS_DEPLOYMENT_TARGET]) -+# XROS_DEPLOYMENT_TARGET should get exported -+AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET]) - - # checks for alternative programs - -@@ -979,11 +1161,19 @@ - ], - ) - --dnl Add the compiler flag for the iOS minimum supported OS version. -+dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS -+dnl version. visionOS doesn't use an explicit -mxros-version-min option - -+dnl it encodes the min version into the target triple. - AS_CASE([$ac_sys_system], - [iOS], [ - AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) - AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) -+ ],[tvOS], [ -+ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) -+ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) -+ ],[watchOS], [ -+ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) -+ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) - ], - ) - -@@ -1172,6 +1362,9 @@ - AS_CASE([$ac_sys_system], - [Darwin*], [MULTIARCH=""], - [iOS], [MULTIARCH=""], -+ [tvOS], [MULTIARCH=""], -+ [watchOS], [MULTIARCH=""], -+ [visionOS], [MULTIARCH=""], - [FreeBSD*], [MULTIARCH=""], - [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] - ) -@@ -1193,7 +1386,7 @@ - dnl use a single "fat" binary at runtime. SOABI_PLATFORM is the component of - dnl the PLATFORM_TRIPLET that will be used in binary module extensions. - AS_CASE([$ac_sys_system], -- [iOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], -+ [iOS|tvOS|watchOS|visionOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], - [SOABI_PLATFORM=$PLATFORM_TRIPLET] - ) - -@@ -1220,16 +1413,22 @@ - [wasm32-unknown-wasip1/clang], [PY_SUPPORT_TIER=2], dnl WebAssembly System Interface preview1, clang - [x86_64-*-linux-gnu/clang], [PY_SUPPORT_TIER=2], dnl Linux on AMD64, any vendor, glibc, clang - -- [aarch64-pc-windows-msvc/msvc], [PY_SUPPORT_TIER=3], dnl Windows ARM64, MSVC -- [armv7l-*-linux-gnueabihf/gcc], [PY_SUPPORT_TIER=3], dnl ARMv7 LE with hardware floats, any vendor, glibc, gcc -- [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*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 -- [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS 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 -- [wasm32-*-emscripten/emcc], [PY_SUPPORT_TIER=3], dnl Emscripten -+ [aarch64-pc-windows-msvc/msvc], [PY_SUPPORT_TIER=3], dnl Windows ARM64, MSVC -+ [armv7l-*-linux-gnueabihf/gcc], [PY_SUPPORT_TIER=3], dnl ARMv7 LE with hardware floats, any vendor, glibc, gcc -+ [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*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 -+ [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS on ARM64 -+ [aarch64-apple-tvos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl tvOS Simulator on arm64 -+ [aarch64-apple-tvos*/clang], [PY_SUPPORT_TIER=3], dnl tvOS on ARM64 -+ [aarch64-apple-watchos*-simulator/clang], [PY_SUPPORT_TIER=3], dnl watchOS Simulator on arm64 -+ [arm64_32-apple-watchos*/clang], [PY_SUPPORT_TIER=3], dnl watchOS on ARM64 -+ [aarch64-apple-xros*-simulator/clang], [PY_SUPPORT_TIER=3], dnl visionOS Simulator on arm64 -+ [aarch64-apple-xros*/clang], [PY_SUPPORT_TIER=3], dnl visionOS on ARM64 -+ [aarch64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on ARM64 -+ [x86_64-*-linux-android/clang], [PY_SUPPORT_TIER=3], dnl Android on AMD64 -+ [wasm32-*-emscripten/emcc], [PY_SUPPORT_TIER=3], dnl Emscripten - - [PY_SUPPORT_TIER=0] - ) -@@ -1537,7 +1736,7 @@ - case $ac_sys_system in - Darwin) - LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; -- iOS) -+ iOS|tvOS|watchOS|visionOS) - LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; - *) - AC_MSG_ERROR([Unknown platform for framework build]);; -@@ -1602,7 +1801,7 @@ - BLDLIBRARY='-L. -lpython$(LDVERSION)' - RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} - ;; -- iOS) -+ iOS|tvOS|watchOS|visionOS) - LDLIBRARY='libpython$(LDVERSION).dylib' - ;; - AIX*) -@@ -3469,7 +3668,7 @@ - BLDSHARED="$LDSHARED" - fi - ;; -- iOS/*) -+ iOS/*|tvOS/*|watchOS/*|visionOS/*) - LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' - LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' - BLDSHARED="$LDSHARED" -@@ -3593,7 +3792,7 @@ - Linux-android*) LINKFORSHARED="-pie -Xlinker -export-dynamic";; - Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; - # -u libsys_s pulls in all symbols in libsys -- Darwin/*|iOS/*) -+ Darwin/*|iOS/*|tvOS/*|watchOS/*|visionOS/*) - LINKFORSHARED="$extra_undefs -framework CoreFoundation" - - # Issue #18075: the default maximum stack size (8MBytes) is too -@@ -3617,7 +3816,7 @@ - LINKFORSHARED="$LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' - fi - LINKFORSHARED="$LINKFORSHARED" -- elif test $ac_sys_system = "iOS"; then -+ elif test "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS"; then - LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' - fi - ;; -@@ -4105,7 +4304,7 @@ - dnl when do we need USING_APPLE_OS_LIBFFI? - ctypes_malloc_closure=yes - ], -- [iOS], [ -+ [iOS|tvOS|watchOS|visionOS], [ - ctypes_malloc_closure=yes - ], - [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] -@@ -5214,9 +5413,9 @@ - # 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 getlogin_r \ - getpeername getpgid getpid getppid getpriority _getpty \ -@@ -5224,8 +5423,7 @@ - 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 -@@ -5235,7 +5433,7 @@ - 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 \ -@@ -5250,12 +5448,20 @@ - AC_CHECK_FUNCS([lchmod]) - fi - --# iOS defines some system methods that can be linked (so they are -+# iOS/tvOS/watchOS/visionOS define some system methods that can be linked (so they are - # found by configure), but either raise a compilation error (because the - # header definition prevents usage - autoconf doesn't use the headers), or - # raise an error if used at runtime. Force these symbols off. --if test "$ac_sys_system" != "iOS" ; then -- AC_CHECK_FUNCS([getentropy getgroups system]) -+if test "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" ; then -+ AC_CHECK_FUNCS([ getentropy getgroups system ]) -+fi + -+# tvOS/watchOS have some additional methods that can be found, but not used. -+if test "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" ; then -+ AC_CHECK_FUNCS([ \ -+ execv fork fork1 posix_spawn posix_spawnp posix_spawn_file_actions_addclosefrom_np \ -+ sigaltstack \ -+ ]) - fi - - AC_CHECK_DECL([dirfd], -@@ -5550,20 +5756,22 @@ - [@%:@include ]) - - # 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]) -@@ -5602,10 +5810,10 @@ - ]) - ]) - --# On Android and iOS, clock_settime can be linked (so it is found by -+# On Android, iOS, tvOS, watchOS, and visionOS, clock_settime can be linked (so it is found by - # configure), but when used in an unprivileged process, it crashes rather than - # returning an error. Force the symbol off. --if test "$ac_sys_system" != "Linux-android" && test "$ac_sys_system" != "iOS" -+if test "$ac_sys_system" != "Linux-android" -a "$ac_sys_system" != "iOS" -a "$ac_sys_system" != "tvOS" -a "$ac_sys_system" != "watchOS" -a "$ac_sys_system" != "visionOS" - then - AC_CHECK_FUNCS([clock_settime], [], [ - AC_CHECK_LIB([rt], [clock_settime], [ -@@ -5763,7 +5971,7 @@ - [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" -@@ -6356,8 +6564,8 @@ - LIBPYTHON="\$(BLDLIBRARY)" - fi - --# On iOS the shared libraries must be linked with the Python framework --if test "$ac_sys_system" = "iOS"; then -+# On iOS/tvOS/watchOS the shared libraries must be linked with the Python framework -+if test "$ac_sys_system" = "iOS" -o $ac_sys_system = "tvOS" -o $ac_sys_system = "watchOS" -o $ac_sys_system = "visionOS"; then - MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" - fi - -@@ -7016,7 +7224,7 @@ - dnl NOTE: Inform user how to proceed with files when cross compiling. - dnl Some cross-compile builds are predictable; they won't ever - dnl have /dev/ptmx or /dev/ptc, so we can set them explicitly. --if test "$ac_sys_system" = "Linux-android" || test "$ac_sys_system" = "iOS"; then -+if test "$ac_sys_system" = "Linux-android" -o "$ac_sys_system" = "iOS" -o "$ac_sys_system" = "tvOS" -o "$ac_sys_system" = "watchOS" -o "$ac_sys_system" = "visionOS" ; then - ac_cv_file__dev_ptmx=no - ac_cv_file__dev_ptc=no - else -@@ -7316,7 +7524,7 @@ - AS_CASE([$ac_sys_system], - [Emscripten], [with_ensurepip=no], - [WASI], [with_ensurepip=no], -- [iOS], [with_ensurepip=no], -+ [iOS|tvOS|watchOS|visionOS], [with_ensurepip=no], - [with_ensurepip=upgrade] - ) - ]) -@@ -7703,7 +7911,7 @@ - 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=;; -@@ -7728,7 +7936,7 @@ - [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], - dnl The _scproxy module is available on macOS - [Darwin], [], -- [iOS], [ -+ [iOS|tvOS|watchOS|visionOS], [ - dnl subprocess and multiprocessing are not supported (no fork syscall). - dnl curses and tkinter user interface are not available. - dnl gdbm and nis aren't available -diff --git a/iOS/Resources/Info.plist.in b/iOS/Resources/Info.plist.in -index c3e261ecd9e..26ef7a95de4 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 ---- /dev/null -+++ b/tvOS/README.rst -@@ -0,0 +1,108 @@ -+===================== -+Python on tvOS README -+===================== ++def ci(context: argparse.Namespace) -> None: ++ """The implementation of the "ci" command.""" ++ clean(context, "all") ++ build(context, host="all") ++ test(context, host="all") + -+:Authors: -+ Russell Keith-Magee (2023-11) + -+This document provides a quick overview of some tvOS specific features in the -+Python distribution. ++def parse_args() -> argparse.Namespace: ++ parser = argparse.ArgumentParser( ++ description=( ++ "A tool for managing the build, package and test process of " ++ "CPython on Apple platforms." ++ ), ++ ) ++ parser.suggest_on_error = True ++ subcommands = parser.add_subparsers(dest="subcommand", required=True) + -+Compilers for building on tvOS -+============================== ++ clean = subcommands.add_parser( ++ "clean", ++ help="Delete all build directories", ++ ) + -+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. ++ configure_build = subcommands.add_parser( ++ "configure-build", help="Run `configure` for the build Python" ++ ) ++ subcommands.add_parser( ++ "make-build", help="Run `make` for the build Python" ++ ) ++ configure_host = subcommands.add_parser( ++ "configure-host", ++ help="Run `configure` for a specific platform and target", ++ ) ++ make_host = subcommands.add_parser( ++ "make-host", ++ help="Run `make` for a specific platform and target", ++ ) ++ package = subcommands.add_parser( ++ "package", ++ help="Create a release package for the platform", ++ ) ++ build = subcommands.add_parser( ++ "build", ++ help="Build all platform targets and create the XCframework", ++ ) ++ test = subcommands.add_parser( ++ "test", ++ help="Run the testbed for a specific platform", ++ ) ++ ci = subcommands.add_parser( ++ "ci", ++ help="Run build, package, and test", ++ ) + -+tvOS specific arguments to configure -+=================================== ++ # platform argument ++ for cmd in [clean, configure_host, make_host, package, build, test, ci]: ++ cmd.add_argument( ++ "platform", ++ choices=HOSTS.keys(), ++ help="The target platform to build", ++ ) + -+* ``--enable-framework[=DIR]`` ++ # host triple argument ++ for cmd in [configure_host, make_host]: ++ cmd.add_argument( ++ "host", ++ help="The host triple to build (e.g., arm64-apple-ios-simulator)", ++ ) ++ # optional host triple argument ++ for cmd in [clean, build, test]: ++ cmd.add_argument( ++ "host", ++ nargs="?", ++ default="all", ++ help=( ++ "The host triple to build (e.g., arm64-apple-ios-simulator), " ++ "or 'build' for just the build platform, or 'hosts' for all " ++ "host platforms, or 'all' for the build platform and all " ++ "hosts. Defaults to 'all'" ++ ), ++ ) + -+ This argument specifies the location where the Python.framework will -+ be installed. ++ # --clean option ++ for cmd in [configure_build, configure_host, build, package, test, ci]: ++ cmd.add_argument( ++ "--clean", ++ action="store_true", ++ default=False, ++ dest="clean", ++ help="Delete the relevant build directories first", ++ ) + -+* ``--with-framework-name=NAME`` ++ # --cache-dir option ++ for cmd in [configure_host, build, ci]: ++ cmd.add_argument( ++ "--cache-dir", ++ default="./cross-build/downloads", ++ help="The directory to store cached downloads.", ++ ) + -+ Specify the name for the python framework, defaults to ``Python``. ++ # --simulator option ++ for cmd in [test, ci]: ++ cmd.add_argument( ++ "--simulator", ++ help=( ++ "The name of the simulator to use (eg: 'iPhone 16e'). Defaults to " ++ "the most recently released 'entry level' iPhone device. Device " ++ "architecture and OS version can also be specified; e.g., " ++ "`--simulator 'iPhone 16 Pro,arch=arm64,OS=26.0'` would run on " ++ "an ARM64 iPhone 16 Pro simulator running iOS 26.0." ++ ), ++ ) ++ cmd.add_argument( ++ "--slow", ++ action="store_true", ++ help="Run tests with --slow-ci options.", ++ ) + ++ for subcommand in [configure_build, configure_host, build, ci]: ++ subcommand.add_argument( ++ "args", nargs="*", help="Extra arguments to pass to `configure`" ++ ) + -+Building and using Python on tvOS -+================================= ++ return parser.parse_args() + -+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. ++def print_called_process_error(e: subprocess.CalledProcessError) -> None: ++ for stream_name in ["stdout", "stderr"]: ++ content = getattr(e, stream_name) ++ stream = getattr(sys, stream_name) ++ if content: ++ stream.write(content) ++ if not content.endswith("\n"): ++ stream.write("\n") + -+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.) ++ # shlex uses single quotes, so we surround the command with double quotes. ++ print( ++ f'Command "{join_command(e.cmd)}" returned exit status {e.returncode}' ++ ) + -+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? -+------------------------------- ++def main() -> None: ++ # Handle SIGTERM the same way as SIGINT. This ensures that if we're ++ # terminated by the buildbot worker, we'll make an attempt to clean up our ++ # subprocesses. ++ def signal_handler(*args): ++ os.kill(os.getpid(), signal.SIGINT) ++ ++ signal.signal(signal.SIGTERM, signal_handler) ++ ++ # Process command line arguments ++ context = parse_args() ++ dispatch: dict[str, Callable] = { ++ "clean": clean, ++ "configure-build": configure_build_python, ++ "make-build": make_build_python, ++ "configure-host": configure_host_python, ++ "make-host": make_host_python, ++ "package": package, ++ "build": build, ++ "test": test, ++ "ci": ci, ++ } + -+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: ++ try: ++ dispatch[context.subcommand](context) ++ except CalledProcessError as e: ++ print() ++ print_called_process_error(e) ++ sys.exit(1) ++ except RuntimeError as e: ++ print() ++ print(e) ++ sys.exit(2) + -+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. ++if __name__ == "__main__": ++ main() +--- /dev/null ++++ b/Apple/iOS/README.md +@@ -0,0 +1,328 @@ ++# Python on iOS README + -+The build also requires the use of cross-compilation. The commands for building -+Python for tvOS will look somethign like:: ++**iOS support is [tier 3](https://peps.python.org/pep-0011/#tier-3).** + -+ $ ./configure \ -+ --enable-framework=/path/to/install \ -+ --host=aarch64-apple-tvos \ -+ --build=aarch64-apple-darwin \ -+ --with-build-python=/path/to/python.exe -+ $ make -+ $ make install ++This document provides a quick overview of some iOS specific features in the ++Python distribution. + -+In this invocation: ++These instructions are only needed if you're planning to compile Python for iOS ++yourself. Most users should *not* need to do this. If you're looking to ++experiment with writing an iOS app in Python, tools such as [BeeWare's ++Briefcase](https://briefcase.readthedocs.io) and [Kivy's ++Buildozer](https://buildozer.readthedocs.io) will provide a much more ++approachable user experience. + -+* ``/path/to/install`` is the location where the final Python.framework will be -+ output. ++## Compilers for building on iOS + -+* ``--host`` is the architecture and ABI that you want to build, in GNU compiler ++Building for iOS requires the use of Apple's Xcode tooling. It is strongly ++recommended that you use the most recent stable release of Xcode. This will ++require the use of the most (or second-most) recently released macOS version, ++as Apple does not maintain Xcode for older macOS versions. The Xcode Command ++Line Tools are not sufficient for iOS development; you need a *full* Xcode ++install. ++ ++If you want to run your code on the iOS simulator, you'll also need to install ++an iOS Simulator Platform. You should be prompted to select an iOS Simulator ++Platform when you first run Xcode. Alternatively, you can add an iOS Simulator ++Platform by selecting an open the Platforms tab of the Xcode Settings panel. ++ ++## Building Python on iOS ++ ++### ABIs and Architectures ++ ++iOS apps can be deployed on physical devices, and on the iOS simulator. Although ++the API used on these devices is identical, the ABI is different - you need to ++link against different libraries for an iOS device build (`iphoneos`) or an ++iOS simulator build (`iphonesimulator`). ++ ++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 that share a common API. ++ ++iOS can also support different CPU architectures within each ABI. At present, ++there is only a single supported 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. It is possible to compile and use a "thin" single architecture ++version of a binary for testing purposes; however, the "thin" binary will not be ++portable to machines using other architectures. ++ ++### Building a multi-architecture iOS XCframework ++ ++The `Apple` subfolder of the Python repository acts as a build script that ++can be used to coordinate the compilation of a complete iOS XCframework. To use ++it, run:: ++ ++ python Apple build iOS ++ ++This will: ++ ++* Configure and compile a version of Python to run on the build machine ++* Download pre-compiled binary dependencies for each platform ++* Configure and build a `Python.framework` for each required architecture and ++ iOS SDK ++* Merge the multiple `Python.framework` folders into a single `Python.xcframework` ++* Produce a `.tar.gz` archive in the `cross-build/dist` folder containing ++ the `Python.xcframework`, plus a copy of the Testbed app pre-configured to ++ use the XCframework. ++ ++The `Apple` build script has other entry points that will perform the ++individual parts of the overall `build` target, plus targets to test the ++build, clean the `cross-build` folder of iOS build products, and perform a ++complete "build and test" CI run. The `--clean` flag can also be used on ++individual commands to ensure that a stale build product are removed before ++building. ++ ++### Building a single-architecture framework ++ ++If you're using the `Apple` build script, you won't need to build ++individual frameworks. However, if you do need to manually configure an iOS ++Python build for a single framework, the following options are available. ++ ++#### iOS specific arguments to configure ++ ++* `--enable-framework[=DIR]` ++ ++ This argument specifies the location where the Python.framework will be ++ installed. If `DIR` is not specified, the framework will be installed into ++ a subdirectory of the `iOS/Frameworks` folder. ++ ++ This argument *must* be provided when configuring iOS builds. iOS does not ++ support non-framework builds. ++ ++* `--with-framework-name=NAME` ++ ++ Specify the name for the Python framework; defaults to `Python`. ++ ++ > [!NOTE] ++ > Unless you know what you're doing, changing the name of the Python ++ > framework on iOS is not advised. If you use this option, you won't be able ++ > to run the `Apple` build script without making significant manual ++ > alterations, and you won't be able to use any binary packages unless you ++ > compile them yourself using your own framework name. ++ ++#### Building Python for iOS ++ ++The Python build system will create a `Python.framework` that supports a ++*single* ABI with a *single* architecture. Unlike macOS, iOS does not allow a ++framework to contain non-library content, so the iOS build will produce a ++`bin` and `lib` folder in the same output folder as `Python.framework`. ++The `lib` folder will be needed at runtime to support the Python library. ++ ++If you want to use Python in a real iOS project, you need to produce multiple ++`Python.framework` builds, one for each ABI and architecture. iOS 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 minimal commands for building ++Python for the ARM64 iOS simulator will look something like: ++``` ++export PATH="$(pwd)/Apple/iOS/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin" ++./configure \ ++ --enable-framework \ ++ --host=arm64-apple-ios-simulator \ ++ --build=arm64-apple-darwin \ ++ --with-build-python=/path/to/python.exe ++make ++make install ++``` ++ ++In this invocation: ++ ++* `Apple/iOS/Resources/bin` has been added to the path, providing some shims for the ++ compilers and linkers needed by the build. Xcode requires the use of `xcrun` ++ to invoke compiler tooling. However, if `xcrun` is pre-evaluated and the ++ result passed to `configure`, these results can embed user- and ++ version-specific paths into the sysconfig data, which limits the portability ++ of the compiled Python. Alternatively, if `xcrun` is used *as* the compiler, ++ it requires that compiler variables like `CC` include spaces, which can ++ cause significant problems with many C configuration systems which assume that ++ `CC` will be a single executable. ++ ++ To work around this problem, the `Apple/iOS/Resources/bin` folder contains some ++ wrapper scripts that present as simple compilers and linkers, but wrap ++ underlying calls to `xcrun`. This allows configure to use a `CC` ++ definition without spaces, and without user- or version-specific paths, while ++ retaining the ability to adapt to the local Xcode install. These scripts are ++ included in the `bin` directory of an iOS install. ++ ++ These scripts will, by default, use the currently active Xcode installation. ++ If you want to use a different Xcode installation, you can use ++ `xcode-select` to set a new default Xcode globally, or you can use the ++ `DEVELOPER_DIR` environment variable to specify an Xcode install. The ++ scripts will use the default `iphoneos`/`iphonesimulator` SDK version for ++ the select Xcode install; if you want to use a different SDK, you can set the ++ `IOS_SDK_VERSION` environment variable. (e.g, setting ++ `IOS_SDK_VERSION=17.1` would cause the scripts to use the `iphoneos17.1` ++ and `iphonesimulator17.1` SDKs, regardless of the Xcode default.) ++ ++ The path has also been cleared of any user customizations. A common source of ++ bugs is for tools like Homebrew to accidentally leak macOS binaries into an iOS ++ build. Resetting the path to a known "bare bones" value is the easiest way to ++ avoid these problems. ++ ++* `--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 ++ - `arm64-apple-ios` for ARM64 iOS devices. ++ - `arm64-apple-ios-simulator` for the iOS simulator running on Apple + Silicon devices. -+ - ``x86_64-apple-tvos-simulator`` for the tvOS simulator running on Intel ++ - `x86_64-apple-ios-simulator` for the iOS simulator running on Intel + devices. + -+* ``--build`` is the GNU compiler triple for the machine that will be running ++* `--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. ++ - `arm64-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 ++* `/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 ++ run Python code. However, the binaries produced for iOS 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 -+====================================== ++ the same version as the Python that is being compiled. To be completely safe, ++ this should be the *exact* same commit hash. However, the longer a Python ++ release has been stable, the more likely it is that this constraint can be ++ relaxed - the same micro version will often be sufficient. ++ ++* The `install` target for iOS builds is slightly different to other ++ platforms. On most platforms, `make install` will install the build into ++ the final runtime location. This won't be the case for iOS, as the final ++ runtime location will be on a physical device. ++ ++ However, you still need to run the `install` target for iOS builds, as it ++ performs some final framework assembly steps. The location specified with ++ `--enable-framework` will be the location where `make install` will ++ assemble the complete iOS framework. This completed framework can then ++ be copied and relocated as required. ++ ++For a full CPython build, you also need to specify the paths to iOS builds of ++the binary libraries that CPython depends on (such as XZ, LibFFI and OpenSSL). ++This can be done by defining library specific environment variables (such as ++`LIBLZMA_CFLAGS`, `LIBLZMA_LIBS`), and the `--with-openssl` configure ++option. Versions of these libraries pre-compiled for iOS can be found in [this ++repository](https://github.com/beeware/cpython-apple-source-deps/releases). ++LibFFI is especially important, as many parts of the standard library ++(including the `platform`, `sysconfig` and `webbrowser` modules) require ++the use of the `ctypes` module at runtime. ++ ++By default, Python will be compiled with an iOS deployment target (i.e., the ++minimum supported iOS version) of 13.0. To specify a different deployment ++target, provide the version number as part of the `--host` argument - for ++example, `--host=arm64-apple-ios15.4-simulator` would compile an ARM64 ++simulator build with a deployment target of 15.4. ++ ++## Testing Python on iOS ++ ++### Testing a multi-architecture framework ++ ++Once you have a built an XCframework, you can test that framework by running: ++ ++ $ python Apple test iOS ++ ++### Testing a single-architecture framework ++ ++The `Apple/testbed` folder that contains an Xcode project that is able to run ++the Python test suite on Apple platforms. This project converts the Python test ++suite into a single test case in Xcode's XCTest framework. The single XCTest ++passes if the test suite passes. ++ ++To run the test suite, configure a Python build for an iOS simulator (i.e., ++`--host=arm64-apple-ios-simulator` or `--host=x86_64-apple-ios-simulator` ++), specifying a framework build (i.e. `--enable-framework`). Ensure that your ++`PATH` has been configured to include the `Apple/iOS/Resources/bin` folder and ++exclude any non-iOS tools, then run: ++``` ++make all ++make install ++make testios ++``` ++ ++This will: ++ ++* Build an iOS framework for your chosen architecture; ++* Finalize the single-platform framework; ++* Make a clean copy of the testbed project; ++* Install the Python iOS framework into the copy of the testbed project; and ++* Run the test suite on an "entry-level device" simulator (i.e., an iPhone SE, ++ iPhone 16e, or a similar). ++ ++On success, the test suite will exit and report successful completion of the ++test suite. On a 2022 M1 MacBook Pro, the test suite takes approximately 15 ++minutes to run; a couple of extra minutes is required to compile the testbed ++project, and then boot and prepare the iOS simulator. ++ ++### Debugging test failures ++ ++Running `python Apple test iOS` generates a standalone version of the ++`Apple/testbed` project, and runs the full test suite. It does this using ++`Apple/testbed` itself - the folder is an executable module that can be used ++to create and run a clone of the testbed project. The standalone version of the ++testbed will be created in a directory named ++`cross-build/iOS-testbed.`. ++ ++You can generate your own standalone testbed instance by running: ++``` ++python cross-build/iOS/testbed clone my-testbed ++``` ++ ++In this invocation, `my-testbed` is the name of the folder for the new ++testbed clone. ++ ++If you've built your own XCframework, or you only want to test a single architecture, ++you can construct a standalone testbed instance by running: ++``` ++python Apple/testbed clone --platform iOS --framework my-testbed ++``` ++ ++The framework path can be the path path to a `Python.xcframework`, or the ++path to a folder that contains a single-platform `Python.framework`. ++ ++You can then use the `my-testbed` folder to run the Python test suite, ++passing in any command line arguments you may require. For example, if you're ++trying to diagnose a failure in the `os` module, you might run: ++``` ++python my-testbed run -- test -W test_os ++``` ++ ++This is the equivalent of running `python -m test -W test_os` on a desktop ++Python build. Any arguments after the `--` will be passed to testbed as if ++they were arguments to `python -m` on a desktop machine. ++ ++### Testing in Xcode ++ ++You can also open the testbed project in Xcode by running: ++``` ++open my-testbed/iOSTestbed.xcodeproj ++``` ++ ++This will allow you to use the full Xcode suite of tools for debugging. ++ ++The arguments used to run the test suite are defined as part of the test plan. ++To modify the test plan, select the test plan node of the project tree (it ++should be the first child of the root node), and select the "Configurations" ++tab. Modify the "Arguments Passed On Launch" value to change the testing ++arguments. ++ ++The test plan also disables parallel testing, and specifies the use of the ++`Testbed.lldbinit` file for providing configuration of the debugger. The ++default debugger configuration disables automatic breakpoints on the ++`SIGINT`, `SIGUSR1`, `SIGUSR2`, and `SIGXFSZ` signals. ++ ++### Testing on an iOS device ++ ++To test on an iOS device, the app needs to be signed with known developer ++credentials. To obtain these credentials, you must have an iOS Developer ++account, and your Xcode install will need to be logged into your account (see ++the Accounts tab of the Preferences dialog). ++ ++Once the project is open, and you're signed into your Apple Developer account, ++select the root node of the project tree (labeled "iOSTestbed"), then the ++"Signing & Capabilities" tab in the details page. Select a development team ++(this will likely be your own name), and plug in a physical device to your ++macOS machine with a USB cable. You should then be able to select your physical ++device from the list of targets in the pulldown in the Xcode titlebar. --- /dev/null -+++ b/tvOS/Resources/Info.plist.in ++++ b/Apple/iOS/Resources/Info.plist.in @@ -0,0 +1,34 @@ + + @@ -1994,106 +1417,92 @@ index c3e261ecd9e..26ef7a95de4 100644 + CFBundleSignature + ???? + CFBundleVersion -+ 1 ++ %VERSION% + CFBundleSupportedPlatforms + -+ tvOS ++ iPhoneOS + + MinimumOSVersion -+ @TVOS_DEPLOYMENT_TARGET@ ++ @IPHONEOS_DEPLOYMENT_TARGET@ + + --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-ar ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-ar @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} ar "$@" ++#!/bin/sh ++xcrun --sdk iphoneos${IOS_SDK_VERSION} ar "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-clang ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-clang @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos "$@" ++#!/bin/sh ++xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET} "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-clang++ ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-clang++ @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos "$@" ++#!/bin/sh ++xcrun --sdk iphoneos${IOS_SDK_VERSION} clang++ -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET} "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-cpp ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-cpp @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos -E "$@" ++#!/bin/sh ++xcrun --sdk iphoneos${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET} -E "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-ar @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-clang++ ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-clang++ @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos-simulator "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator "$@" --- /dev/null -+++ b/tvOS/Resources/bin/arm64-apple-tvos-simulator-cpp ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-cpp @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target arm64-apple-tvos-simulator -E "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target arm64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator -E "$@" --- /dev/null -+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-simulator-strip @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} strip -arch arm64 "$@" --- /dev/null -+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang ++++ b/Apple/iOS/Resources/bin/arm64-apple-ios-strip @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator "$@" ++#!/bin/sh ++xcrun --sdk iphoneos${IOS_SDK_VERSION} strip -arch arm64 "$@" --- /dev/null -+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-clang++ ++++ b/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-ar @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang++ -target x86_64-apple-tvos-simulator "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} ar "$@" --- /dev/null -+++ b/tvOS/Resources/bin/x86_64-apple-tvos-simulator-cpp ++++ b/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-clang @@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} clang -target x86_64-apple-tvos-simulator -E "$@" ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator "$@" --- /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 -+ -+ ++++ b/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang++ -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator "$@" --- /dev/null -+++ b/tvOS/Resources/pyconfig.h ++++ b/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} clang -target x86_64-apple-ios${IPHONEOS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/Apple/iOS/Resources/bin/x86_64-apple-ios-simulator-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk iphonesimulator${IOS_SDK_VERSION} strip -arch x86_64 "$@" +--- /dev/null ++++ b/Apple/iOS/Resources/pyconfig.h @@ -0,0 +1,7 @@ +#ifdef __arm64__ +#include "pyconfig-arm64.h" @@ -2103,84 +1512,415 @@ index c3e261ecd9e..26ef7a95de4 100644 +#include "pyconfig-x86_64.h" +#endif --- /dev/null -+++ b/visionOS/Resources/Info.plist.in -@@ -0,0 +1,34 @@ ++++ b/Apple/testbed/Python.xcframework/Info.plist +@@ -0,0 +1,136 @@ + -+ -+ ++ ++ + -+ CFBundleDevelopmentRegion -+ en -+ CFBundleExecutable -+ Python -+ CFBundleGetInfoString -+ Python Runtime and Library -+ CFBundleIdentifier -+ @PYTHONFRAMEWORKIDENTIFIER@ ++ AvailableLibraries ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ ios-arm64 ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ ++ SupportedPlatform ++ ios ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ ios-arm64_x86_64-simulator ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ x86_64 ++ ++ SupportedPlatform ++ ios ++ SupportedPlatformVariant ++ simulator ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ tvos-arm64 ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ ++ SupportedPlatform ++ tvos ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ tvos-arm64_x86_64-simulator ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ x86_64 ++ ++ SupportedPlatform ++ tvos ++ SupportedPlatformVariant ++ simulator ++ ++ ++ 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 ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ watchos-arm64_x86_64-simulator ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64 ++ x86_64 ++ ++ SupportedPlatform ++ watchos ++ SupportedPlatformVariant ++ simulator ++ ++ ++ BinaryPath ++ Python.framework/Python ++ LibraryIdentifier ++ watchos-arm64_32 ++ LibraryPath ++ Python.framework ++ SupportedArchitectures ++ ++ arm64_32 ++ ++ SupportedPlatform ++ watchos ++ ++ ++ CFBundlePackageType ++ XFWK ++ XCFrameworkFormatVersion ++ 1.0 ++ ++ +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/build/iOS-dylib-Info-template.plist +@@ -0,0 +1,26 @@ ++ ++ ++ ++ ++ CFBundleDevelopmentRegion ++ en ++ CFBundleExecutable ++ ++ CFBundleIdentifier ++ + CFBundleInfoDictionaryVersion + 6.0 -+ CFBundleName -+ Python + CFBundlePackageType -+ FMWK ++ APPL + CFBundleShortVersionString -+ %VERSION% -+ CFBundleLongVersionString -+ %VERSION%, (c) 2001-2023 Python Software Foundation. -+ CFBundleSignature -+ ???? -+ CFBundleVersion -+ %VERSION% ++ 1.0 + CFBundleSupportedPlatforms + -+ XROS ++ iPhoneOS + + MinimumOSVersion -+ @XROS_DEPLOYMENT_TARGET@ ++ 13.0 ++ CFBundleVersion ++ 1 + + --- /dev/null -+++ b/visionOS/Resources/bin/arm64-apple-xros-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk xros${XROS_SDK_VERSION} ar "$@" ---- /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} "$@" ---- /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} "$@" ---- /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 "$@" ---- /dev/null -+++ b/visionOS/Resources/bin/arm64-apple-xros-simulator-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk xrsimulator${XROS_SDK_VERSION} ar "$@" ---- /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 "$@" ++++ b/Apple/testbed/Python.xcframework/build/tvOS-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 ++ ++ --- /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 "$@" ++++ b/Apple/testbed/Python.xcframework/build/utils.sh +@@ -0,0 +1,179 @@ ++# Utility methods for use in an Xcode project. ++# ++# An iOS XCframework cannot include any content other than the library binary ++# and relevant metadata. However, Python requires a standard library at runtime. ++# Therefore, it is necessary to add a build step to an Xcode app target that ++# processes the standard library and puts the content into the final app. ++# ++# In general, these tools will be invoked after bundle resources have been ++# copied into the app, but before framework embedding (and signing). ++# ++# The following is an example script, assuming that: ++# * Python.xcframework is in the root of the project ++# * There is an `app` folder that contains the app code ++# * There is an `app_packages` folder that contains installed Python packages. ++# ----- ++# set -e ++# source $PROJECT_DIR/Python.xcframework/build/build_utils.sh ++# install_python Python.xcframework app app_packages ++# ----- ++ ++# Copy the standard library from the XCframework into the app bundle. ++# ++# Accepts one argument: ++# 1. The path, relative to the root of the Xcode project, where the Python ++# XCframework can be found. ++install_stdlib() { ++ PYTHON_XCFRAMEWORK_PATH=$1 ++ ++ mkdir -p "$CODESIGNING_FOLDER_PATH/python/lib" ++ if [ "$EFFECTIVE_PLATFORM_NAME" = "-iphonesimulator" ]; then ++ echo "Installing Python modules for iOS Simulator" ++ if [ -d "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/ios-arm64-simulator" ]; then ++ SLICE_FOLDER="ios-arm64-simulator" ++ else ++ SLICE_FOLDER="ios-arm64_x86_64-simulator" ++ fi ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-iphoneos" ]; then ++ echo "Installing Python modules for iOS Device" ++ SLICE_FOLDER="ios-arm64" ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-appletvsimulator" ]; then ++ echo "Installing Python modules for tvOS Simulator" ++ if [ -d "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/tvos-arm64-simulator" ]; then ++ SLICE_FOLDER="tvos-arm64-simulator" ++ else ++ SLICE_FOLDER="tvos-arm64_x86_64-simulator" ++ fi ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-appletvos" ]; then ++ echo "Installing Python modules for tvOS Device" ++ SLICE_FOLDER="tvos-arm64" ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-watchsimulator" ]; then ++ echo "Installing Python modules for watchOS Simulator" ++ if [ -d "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/watchos-arm64-simulator" ]; then ++ SLICE_FOLDER="watchos-arm64-simulator" ++ else ++ SLICE_FOLDER="watchos-arm64_x86_64-simulator" ++ fi ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-watchos" ]; then ++ echo "Installing Python modules for watchOS Device" ++ SLICE_FOLDER="watchos-arm64" ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-xrsimulator" ]; then ++ echo "Installing Python modules for visionOS Simulator" ++ SLICE_FOLDER="xros-arm64-simulator" ++ elif [ "$EFFECTIVE_PLATFORM_NAME" = "-xros" ]; then ++ echo "Installing Python modules for visionOS Device" ++ SLICE_FOLDER="xros-arm64" ++ else ++ echo "Unsupported platform name $EFFECTIVE_PLATFORM_NAME" ++ exit 1 ++ fi ++ ++ # If the XCframework has a shared lib folder, then it's a full framework. ++ # Copy both the common and slice-specific part of the lib directory. ++ # Otherwise, it's a single-arch framework; use the "full" lib folder. ++ if [ -d "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/lib" ]; then ++ rsync -au --delete "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" ++ rsync -au "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/$SLICE_FOLDER/lib-$ARCHS/" "$CODESIGNING_FOLDER_PATH/python/lib/" ++ else ++ rsync -au --delete "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/$SLICE_FOLDER/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" ++ fi ++} ++ ++# Convert a single .so library into a framework that iOS can load. ++# ++# Accepts three arguments: ++# 1. The path, relative to the root of the Xcode project, where the Python ++# XCframework can be found. ++# 2. The base path, relative to the installed location in the app bundle, that ++# needs to be processed. Any .so file found in this path (or a subdirectory ++# of it) will be processed. ++# 2. The full path to a single .so file to process. This path should include ++# the base path. ++install_dylib () { ++ PYTHON_XCFRAMEWORK_PATH=$1 ++ INSTALL_BASE=$2 ++ FULL_EXT=$3 ++ ++ # The name of the extension file ++ EXT=$(basename "$FULL_EXT") ++ # The name and location of the module ++ MODULE_PATH=$(dirname "$FULL_EXT") ++ MODULE_NAME=$(echo $EXT | cut -d "." -f 1) ++ # The location of the extension file, relative to the bundle ++ RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} ++ # The path to the extension file, relative to the install base ++ PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/} ++ # The full dotted name of the extension module, constructed from the file path. ++ FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d "." -f 1 | tr "/" "."); ++ # A bundle identifier; not actually used, but required by Xcode framework packaging ++ FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-") ++ # The name of the framework folder. ++ FRAMEWORK_FOLDER="Frameworks/$FULL_MODULE_NAME.framework" ++ ++ # If the framework folder doesn't exist, create it. ++ if [ ! -d "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ]; then ++ echo "Creating framework for $RELATIVE_EXT" ++ mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ++ cp "$PROJECT_DIR/$PYTHON_XCFRAMEWORK_PATH/build/$PLATFORM_FAMILY_NAME-dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" ++ plutil -replace CFBundleExecutable -string "$FULL_MODULE_NAME" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" ++ plutil -replace CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" ++ fi ++ ++ echo "Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME" ++ mv "$FULL_EXT" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" ++ # Create a placeholder .fwork file where the .so was ++ echo "$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" > ${FULL_EXT%.so}.fwork ++ # Create a back reference to the .so file location in the framework ++ echo "${RELATIVE_EXT%.so}.fwork" > "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin" ++ ++ # If the framework provides an xcprivacy file, install it. ++ if [ -e "$MODULE_PATH/$MODULE_NAME.xcprivacy" ]; then ++ echo "Installing XCPrivacy file for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME" ++ XCPRIVACY_FILE="$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/PrivacyInfo.xcprivacy" ++ if [ -e "$XCPRIVACY_FILE" ]; then ++ rm -rf "$XCPRIVACY_FILE" ++ fi ++ mv "$MODULE_PATH/$MODULE_NAME.xcprivacy" "$XCPRIVACY_FILE" ++ fi ++ ++ echo "Signing framework as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)..." ++ /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 "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ++} ++ ++# Process all the dynamic libraries in a path into Framework format. ++# ++# Accepts two arguments: ++# 1. The path, relative to the root of the Xcode project, where the Python ++# XCframework can be found. ++# 2. The base path, relative to the installed location in the app bundle, that ++# needs to be processed. Any .so file found in this path (or a subdirectory ++# of it) will be processed. ++process_dylibs () { ++ PYTHON_XCFRAMEWORK_PATH=$1 ++ LIB_PATH=$2 ++ find "$CODESIGNING_FOLDER_PATH/$LIB_PATH" -name "*.so" | while read FULL_EXT; do ++ install_dylib $PYTHON_XCFRAMEWORK_PATH "$LIB_PATH/" "$FULL_EXT" ++ done ++} ++ ++# The entry point for post-processing a Python XCframework. ++# ++# Accepts 1 or more arguments: ++# 1. The path, relative to the root of the Xcode project, where the Python ++# XCframework can be found. If the XCframework is in the root of the project, ++# 2+. The path of a package, relative to the root of the packaged app, that contains ++# library content that should be processed for binary libraries. ++install_python() { ++ PYTHON_XCFRAMEWORK_PATH=$1 ++ shift ++ ++ install_stdlib $PYTHON_XCFRAMEWORK_PATH ++ PYTHON_VER=$(ls -1 "$CODESIGNING_FOLDER_PATH/python/lib") ++ echo "Install Python $PYTHON_VER standard library extension modules..." ++ process_dylibs $PYTHON_XCFRAMEWORK_PATH python/lib/$PYTHON_VER/lib-dynload ++ ++ for package_path in $@; do ++ echo "Installing $package_path extension modules ..." ++ process_dylibs $PYTHON_XCFRAMEWORK_PATH $package_path ++ done ++} --- /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 "$@" ++++ b/Apple/testbed/Python.xcframework/build/watchOS-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 ++ ++ --- /dev/null -+++ b/visionOS/Resources/dylib-Info-template.plist ++++ b/Apple/testbed/Python.xcframework/build/xrOS-dylib-Info-template.plist @@ -0,0 +1,30 @@ + + @@ -2213,320 +1953,408 @@ index c3e261ecd9e..26ef7a95de4 100644 + + --- /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 -+ -+ ++++ b/Apple/testbed/Python.xcframework/ios-arm64/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling an iOS on-device ++build for testing purposes. +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/ios-arm64_x86_64-simulator/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling an iOS simulator ++build for testing purposes (either x86_64 or ARM64). +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/tvos-arm64/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling a tvOS ++on-device build for testing purposes. +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/tvos-arm64_x86_64-simulator/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling a tvOS ++simulator build for testing purposes (either x86_64 or ARM64). +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/watchos-arm64_32/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling a watchOS on-device ++build for testing purposes. +--- /dev/null ++++ b/Apple/testbed/Python.xcframework/watchos-arm64_x86_64-simulator/README +@@ -0,0 +1,4 @@ ++This directory is intentionally empty. ++ ++It should be used as a target for `--enable-framework` when compiling a watchOS ++simulator build for testing purposes (either x86_64 or ARM64). --- /dev/null -+++ b/visionOS/testbed/Python.xcframework/xros-arm64-simulator/README ++++ b/Apple/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). --- /dev/null -+++ b/visionOS/testbed/Python.xcframework/xros-arm64/README ++++ b/Apple/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. --- /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 ++++ b/Apple/testbed/Testbed.lldbinit +@@ -0,0 +1,4 @@ ++process handle SIGINT -n true -p true -s false ++process handle SIGUSR1 -n true -p true -s false ++process handle SIGUSR2 -n true -p true -s false ++process handle SIGXFSZ -n true -p true -s false +--- /dev/null ++++ b/Apple/testbed/TestbedTests/TestbedTests.m +@@ -0,0 +1,197 @@ ++#import ++#import + ++@interface TestbedTests : XCTestCase + -+DECODE_ARGS = ("UTF-8", "backslashreplace") ++@end + -+# 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) ... ++@implementation TestbedTests + -+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 -+) + ++- (void)testPython { ++ const char **argv; ++ int exit_code; ++ int failed; ++ PyStatus status; ++ PyPreConfig preconfig; ++ PyConfig config; ++ PyObject *app_packages_path; ++ PyObject *method_args; ++ PyObject *result; ++ PyObject *site_module; ++ PyObject *site_addsitedir_attr; ++ 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` ++ // The processInfo arguments contain the binary that is running, ++ // followed by the arguments defined in the test plan. This means: ++ // run_module = test_args[1] ++ // argv = ["Testbed"] + test_args[2:] ++ test_args = [[NSProcessInfo processInfo] arguments]; ++ if (test_args == NULL) { ++ NSLog(@"Unable to identify test arguments."); ++ } ++ NSLog(@"Test arguments: %@", test_args); ++ argv = malloc(sizeof(char *) * ([test_args count] - 1)); ++ argv[0] = "Testbed"; ++ for (int i = 1; i < [test_args count] - 1; i++) { ++ argv[i] = [[test_args objectAtIndex:i+1] UTF8String]; ++ } ++ ++ // 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:1] 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] - 1, (char**) argv); ++ if (PyStatus_Exception(status)) { ++ XCTFail(@"Unable to configure argc/argv: %s", status.err_msg); ++ PyConfig_Clear(&config); ++ return; ++ } + -+# Work around a bug involving sys.exit and TaskGroups -+# (https://github.com/python/cpython/issues/101515). -+def exit(*args): -+ raise MySystemExit(*args) ++ 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; ++ } + ++ // Add app_packages as a site directory. This both adds to sys.path, ++ // and ensures that any .pth files in that directory will be executed. ++ site_module = PyImport_ImportModule("site"); ++ if (site_module == NULL) { ++ XCTFail(@"Could not import site module"); ++ return; ++ } + -+class MySystemExit(Exception): -+ pass ++ site_addsitedir_attr = PyObject_GetAttrString(site_module, "addsitedir"); ++ if (site_addsitedir_attr == NULL || !PyCallable_Check(site_addsitedir_attr)) { ++ XCTFail(@"Could not access site.addsitedir"); ++ return; ++ } + ++ path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil]; ++ NSLog(@"App packages path: %@", path); ++ wtmp_str = Py_DecodeLocale([path UTF8String], NULL); ++ app_packages_path = PyUnicode_FromWideChar(wtmp_str, wcslen(wtmp_str)); ++ if (app_packages_path == NULL) { ++ XCTFail(@"Could not convert app_packages path to unicode"); ++ return; ++ } ++ PyMem_RawFree(wtmp_str); + -+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 ++ method_args = Py_BuildValue("(O)", app_packages_path); ++ if (method_args == NULL) { ++ XCTFail(@"Could not create arguments for site.addsitedir"); ++ return; ++ } + -+ self.fd = None ++ result = PyObject_CallObject(site_addsitedir_attr, method_args); ++ if (result == NULL) { ++ XCTFail(@"Could not add app_packages directory using site.addsitedir"); ++ return; ++ } + -+ async def acquire(self): -+ # Ensure the lockfile exists -+ self.filename.touch(exist_ok=True) ++ // Add test code to sys.path ++ sys_module = PyImport_ImportModule("sys"); ++ if (sys_module == NULL) { ++ XCTFail(@"Could not import sys module"); ++ return; ++ } + -+ # 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 ++ sys_path_attr = PyObject_GetAttrString(sys_module, "path"); ++ if (sys_path_attr == NULL) { ++ XCTFail(@"Could not access sys.path"); ++ 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() ++ 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); + -+ # 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) ++ // 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(@"---------------------------------------------------------------------------"); + -+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), -+ ) ++ exit_code = Py_RunMain(); ++ XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); + ++ NSLog(@"---------------------------------------------------------------------------"); + -+# 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}") ++ Py_Finalize(); ++} + + -+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) ++@end +--- /dev/null ++++ b/Apple/testbed/__main__.py +@@ -0,0 +1,436 @@ ++import argparse ++import json ++import re ++import shutil ++import subprocess ++import sys ++from pathlib import Path + -+ # 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"' -+ ), -+ ] ++TEST_SLICES = { ++ "iOS": "ios-arm64_x86_64-simulator", ++ "tvOS": "tvos-arm64_x86_64-simulator", ++ "visionOS": "xros-arm64-simulator", ++ "watchOS": "watchos-arm64_x86_64-simulator", ++} + -+ 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() ++DECODE_ARGS = ("UTF-8", "backslashreplace") ++ ++# The system log prefixes each line: ++# 2025-01-17 16:14:29.093742+0800 iOSTestbed[23987:1fd393b4] ... ++# 2025-01-17 16:14:29.093742+0800 iOSTestbed[23987:1fd393b4] ... ++ ++LOG_PREFIX_REGEX = re.compile( ++ r"^\d{4}-\d{2}-\d{2}" # YYYY-MM-DD ++ r"\s+\d+:\d{2}:\d{2}\.\d+\+\d{4}" # HH:MM:SS.ssssss+ZZZZ ++ r"\s+.*Testbed\[\d+:\w+\]" # Process/thread ID ++) ++ ++ ++# Select a simulator device to use. ++def select_simulator_device(platform): ++ # List the testing simulators, in JSON format ++ raw_json = subprocess.check_output(["xcrun", "simctl", "list", "-j"]) ++ json_data = json.loads(raw_json) ++ ++ if platform == "iOS": ++ # Any iOS device will do; we'll look for "SE" devices - but the name isn't ++ # consistent over time. Older Xcode versions will use "iPhone SE (Nth ++ # generation)"; As of 2025, they've started using "iPhone 16e". ++ # ++ # When Xcode is updated after a new release, new devices will be available ++ # and old ones will be dropped from the set available on the latest iOS ++ # version. Select the one with the highest minimum runtime version - this ++ # is an indicator of the "newest" released device, which should always be ++ # supported on the "most recent" iOS version. ++ se_simulators = sorted( ++ (devicetype["minRuntimeVersion"], devicetype["name"]) ++ for devicetype in json_data["devicetypes"] ++ if devicetype["productFamily"] == "iPhone" ++ and ( ++ ( ++ "iPhone " in devicetype["name"] ++ and devicetype["name"].endswith("e") ++ ) ++ or "iPhone SE " in devicetype["name"] ++ ) ++ ) ++ simulator = se_simulators[-1][1] ++ elif platform == "tvOS": ++ # Find the most recent tvOS release. ++ simulators = sorted( ++ (devicetype["minRuntimeVersion"], devicetype["name"]) ++ for devicetype in json_data["devicetypes"] ++ if devicetype["productFamily"] == "Apple TV" ++ ) ++ simulator = simulators[-1][1] ++ elif platform == "visionOS": ++ # Find the most recent visionOS release. ++ simulators = sorted( ++ (devicetype["minRuntimeVersion"], devicetype["name"]) ++ for devicetype in json_data["devicetypes"] ++ if devicetype["productFamily"] == "Apple Vision" ++ ) ++ simulator = simulators[-1][1] ++ elif platform == "watchOS": ++ raise NotImplementedError(f"Don't know how to launch watchOS (yet)") ++ else: ++ raise ValueError(f"Unknown platform {platform}") ++ ++ return simulator + + -+async def xcode_test(location, simulator, verbose): -+ # Run the test suite on the named simulator -+ print("Starting xcodebuild...", flush=True) ++def xcode_test(location: Path, platform: str, simulator: str, verbose: bool): ++ # Build and run the test suite on the named simulator. + args = [ -+ "xcodebuild", -+ "test", + "-project", -+ str(location / "visionOSTestbed.xcodeproj"), ++ str(location / f"{platform}Testbed.xcodeproj"), + "-scheme", -+ "visionOSTestbed", ++ f"{platform}Testbed", + "-destination", -+ f"platform=visionOS Simulator,name={simulator}", -+ "-resultBundlePath", -+ str(location / f"{datetime.now():%Y%m%d-%H%M%S}.xcresult"), ++ f"platform={platform} Simulator,name={simulator}", + "-derivedDataPath", + str(location / "DerivedData"), + ] -+ if not verbose: -+ args += ["-quiet"] ++ verbosity_args = [] if verbose else ["-quiet"] ++ ++ print("Building test project...") ++ subprocess.run( ++ ["xcodebuild", "build-for-testing"] + args + verbosity_args, ++ check=True, ++ ) + -+ async with async_process( -+ *args, ++ print("Running test project...") ++ # Test execution *can't* be run -quiet; verbose mode ++ # is how we see the output of the test output. ++ process = subprocess.Popen( ++ ["xcodebuild", "test-without-building"] + 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() ++ ) ++ while line := (process.stdout.readline()).decode(*DECODE_ARGS): ++ # Strip the timestamp/process prefix from each log line ++ line = LOG_PREFIX_REGEX.sub("", line) ++ sys.stdout.write(line) ++ sys.stdout.flush() ++ ++ status = process.wait(timeout=5) ++ exit(status) ++ ++ ++def copy(src, tgt): ++ """An all-purpose copy. + -+ status = await asyncio.wait_for(process.wait(), timeout=1) -+ exit(status) ++ If src is a file, it is copied. If src is a symlink, it is copied *as a ++ symlink*. If src is a directory, the full tree is duplicated, with symlinks ++ being preserved. ++ """ ++ if src.is_file() or src.is_symlink(): ++ shutil.copyfile(src, tgt, follow_symlinks=False) ++ else: ++ shutil.copytree(src, tgt, symlinks=True) + + +def clone_testbed( + source: Path, + target: Path, + framework: Path, ++ platform: str, + apps: list[Path], +) -> None: + if target.exists(): @@ -2535,11 +2363,11 @@ index c3e261ecd9e..26ef7a95de4 100644 + + if framework is None: + if not ( -+ source / "Python.xcframework/xros-arm64-simulator/bin" ++ source / "Python.xcframework" / TEST_SLICES[platform] / "bin" + ).is_dir(): + print( + f"The testbed being cloned ({source}) does not contain " -+ f"a simulator framework. Re-run with --framework" ++ "a framework with slices. Re-run with --framework" + ) + sys.exit(11) + else: @@ -2557,77 +2385,90 @@ index c3e261ecd9e..26ef7a95de4 100644 + sys.exit(13) + + print("Cloning testbed project:") -+ print(f" Cloning {source}...", end="", flush=True) -+ shutil.copytree(source, target, symlinks=True) ++ print(f" Cloning {source}...", end="") ++ # Only copy the files for the platform being cloned plus the files common ++ # to all platforms. The XCframework will be copied later, if needed. ++ target.mkdir(parents=True) ++ ++ for name in [ ++ "__main__.py", ++ "TestbedTests", ++ "Testbed.lldbinit", ++ f"{platform}Testbed", ++ f"{platform}Testbed.xcodeproj", ++ f"{platform}Testbed.xctestplan", ++ ]: ++ copy(source / name, target / name) ++ + print(" done") + ++ orig_xc_framework_path = source / "Python.xcframework" + xc_framework_path = target / "Python.xcframework" -+ sim_framework_path = xc_framework_path / "xros-arm64-simulator" ++ test_framework_path = xc_framework_path / TEST_SLICES[platform] + 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) ++ print(" Installing XCFramework...", end="") + 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) ++ print(" Installing simulator framework...", end="") ++ # We're only installing a slice of a framework; we need ++ # to do a full tree copy to make sure we don't damage ++ # symlinked content. ++ shutil.copytree(orig_xc_framework_path, xc_framework_path) ++ if test_framework_path.is_dir(): ++ shutil.rmtree(test_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) ++ test_framework_path.unlink(missing_ok=True) ++ test_framework_path.symlink_to( ++ framework.relative_to(test_framework_path.parent, walk_up=True) + ) + print(" done") + else: ++ copy(orig_xc_framework_path, xc_framework_path) ++ + 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() ++ print(" Rewriting symlink to XCframework...", end="") ++ resolved_xc_framework_path = ( ++ source / xc_framework_path.readlink() + ).resolve() + xc_framework_path.unlink() + xc_framework_path.symlink_to( -+ orig_xc_framework_path.relative_to( ++ resolved_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() ++ test_framework_path.is_symlink() ++ and not test_framework_path.readlink().is_absolute() + ): -+ print(" Rewriting symlink to simulator framework...", end="", flush=True) ++ print(" Rewriting symlink to simulator framework...", end="") + # 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() ++ orig_test_framework_path = ( ++ source / "Python.XCframework" / test_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 ++ test_framework_path.unlink() ++ test_framework_path.symlink_to( ++ orig_test_framework_path.relative_to( ++ test_framework_path.parent, walk_up=True + ) + ) + print(" done") + else: -+ print(" Using pre-existing visionOS framework.") ++ print(" Using pre-existing Python 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}" ++ print(f" Installing app {app_src.name!r}...", end="") ++ app_target = target / f"Testbed/app/{app_src.name}" + if app_target.is_dir(): + shutil.rmtree(app_target) + shutil.copytree(app_src, app_target) @@ -2636,65 +2477,64 @@ index c3e261ecd9e..26ef7a95de4 100644 + 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) ++def update_test_plan(testbed_path, platform, args): ++ # Modify the test plan to use the requested test arguments. ++ test_plan_path = testbed_path / f"{platform}Testbed.xctestplan" ++ with test_plan_path.open("r", encoding="utf-8") as f: ++ test_plan = json.load(f) + -+ info["TestArgs"] = args ++ test_plan["defaultOptions"]["commandLineArgumentEntries"] = [ ++ {"argument": arg} for arg in args ++ ] + -+ with info_plist.open("wb") as f: -+ plistlib.dump(info, f) ++ with test_plan_path.open("w", encoding="utf-8") as f: ++ json.dump(test_plan, f, indent=2) + + -+async def run_testbed(simulator: str, args: list[str], verbose: bool=False): ++def run_testbed( ++ platform: str, ++ simulator: str | None, ++ 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() ++ print("Updating test plan...", end="") ++ update_test_plan(location, platform, args) ++ print(" done.") ++ ++ if simulator is None: ++ simulator = select_simulator_device(platform) ++ print(f"Running test on {simulator}") ++ ++ xcode_test( ++ location, ++ platform=platform, ++ simulator=simulator, ++ verbose=verbose, ++ ) + + +def main(): ++ # Look for directories like `iOSTestbed` as an indicator of the platforms ++ # that the testbed folder supports. The original source testbed can support ++ # many platforms, but when cloned, only one platform is preserved. ++ available_platforms = [ ++ platform ++ for platform in ["iOS", "tvOS", "visionOS", "watchOS"] ++ if (Path(__file__).parent / f"{platform}Testbed").is_dir() ++ ] ++ + parser = argparse.ArgumentParser( + description=( -+ "Manages the process of testing a Python project in the visionOS simulator." ++ "Manages the process of testing an Apple Python project through Xcode." + ), + ) + + subcommands = parser.add_subparsers(dest="subcommand") -+ + clone = subcommands.add_parser( + "clone", + description=( -+ "Clone the testbed project, copying in an visionOS Python framework and" ++ "Clone the testbed project, copying in a Python framework and" + "any specified application code." + ), + help="Clone a testbed project to a new location.", @@ -2707,6 +2547,13 @@ index c3e261ecd9e..26ef7a95de4 100644 + ), + ) + clone.add_argument( ++ "--platform", ++ dest="platform", ++ choices=available_platforms, ++ default=available_platforms[0], ++ help=f"The platform to target (default: {available_platforms[0]})", ++ ) ++ clone.add_argument( + "--app", + dest="apps", + action="append", @@ -2723,18 +2570,31 @@ index c3e261ecd9e..26ef7a95de4 100644 + 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 " ++ "passed to the running test process as if they were arguments to " + "`python -m`." + ), + help="Run a testbed project", + ) + run.add_argument( ++ "--platform", ++ dest="platform", ++ choices=available_platforms, ++ default=available_platforms[0], ++ help=f"The platform to target (default: {available_platforms[0]})", ++ ) ++ run.add_argument( + "--simulator", -+ default="Apple Vision Pro", -+ help="The name of the simulator to use (default: 'Apple Vision Pro')", ++ help=( ++ "The name of the simulator to use (eg: 'iPhone 16e'). Defaults to " ++ "the most recently released 'entry level' iPhone device. Device " ++ "architecture and OS version can also be specified; e.g., " ++ "`--simulator 'iPhone 16 Pro,arch=arm64,OS=26.0'` would run on " ++ "an ARM64 iPhone 16 Pro simulator running iOS 26.0." ++ ), + ) + run.add_argument( -+ "-v", "--verbose", ++ "-v", ++ "--verbose", + action="store_true", + help="Enable verbose output", + ) @@ -2753,30 +2613,37 @@ index c3e261ecd9e..26ef7a95de4 100644 + clone_testbed( + source=Path(__file__).parent.resolve(), + target=Path(context.location).resolve(), -+ framework=Path(context.framework).resolve() if context.framework else None, ++ framework=Path(context.framework).resolve() ++ if context.framework ++ else None, ++ platform=context.platform, + 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" ++ Path(__file__).parent ++ / "Python.xcframework" ++ / TEST_SLICES[context.platform] ++ / "bin" + ).is_dir(): + print( -+ f"Testbed does not contain a compiled visionOS framework. Use " ++ f"Testbed does not contain a compiled Python 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, -+ ) ++ run_testbed( ++ platform=context.platform, ++ simulator=context.simulator, ++ verbose=context.verbose, ++ args=test_args, + ) + else: -+ print(f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)") ++ print( ++ f"Must specify test arguments (e.g., {sys.argv[0]} run -- test)" ++ ) + print() + parser.print_help(sys.stderr) + sys.exit(21) @@ -2788,8 +2655,8 @@ index c3e261ecd9e..26ef7a95de4 100644 +if __name__ == "__main__": + main() --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed.xcodeproj/project.pbxproj -@@ -0,0 +1,581 @@ ++++ b/Apple/testbed/iOSTestbed.xcodeproj/project.pbxproj +@@ -0,0 +1,557 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; @@ -2801,15 +2668,15 @@ index c3e261ecd9e..26ef7a95de4 100644 +/* 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 */; }; ++ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */; }; + 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 */; }; ++ 607A66322B0EFA3A0010BFC8 /* TestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */; }; ++ 607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; }; ++ 607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ++ 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; }; ++ 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 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 */ @@ -2829,7 +2696,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + dstPath = ""; + dstSubfolderSpec = 10; + files = ( -+ EEB367CF2DADF5D300B9A1D7 /* Python.xcframework in Embed Frameworks */, ++ 607A664D2B0EFC080010BFC8 /* Python.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; @@ -2840,7 +2707,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + dstPath = ""; + dstSubfolderSpec = 10; + files = ( -+ EEB367CE2DADF5C900B9A1D7 /* Python.xcframework in Embed Frameworks */, ++ 607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; @@ -2848,18 +2715,19 @@ index c3e261ecd9e..26ef7a95de4 100644 +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ -+ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = visionOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66122B0EFA380010BFC8 /* iOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSTestbed.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 = ""; }; ++ 607A66242B0EFA390010BFC8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; 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 = ""; }; ++ 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestbedTests.m; sourceTree = ""; }; ++ 607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; ++ 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 = ""; }; -+ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; ++ 60FE0EFB2E56BB6D00524F87 /* iOSTestbed.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = iOSTestbed.xctestplan; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ @@ -2867,7 +2735,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( -+ EEE9C80D2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */, ++ 607A664C2B0EFC080010BFC8 /* Python.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; @@ -2875,7 +2743,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( -+ EEE9C80E2DAB5ECA0056F8C6 /* Python.xcframework in Frameworks */, ++ 607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; @@ -2885,9 +2753,10 @@ index c3e261ecd9e..26ef7a95de4 100644 + 607A66092B0EFA380010BFC8 = { + isa = PBXGroup; + children = ( -+ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */, -+ 607A66142B0EFA380010BFC8 /* visionOSTestbed */, -+ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */, ++ 60FE0EFB2E56BB6D00524F87 /* iOSTestbed.xctestplan */, ++ 607A664A2B0EFB310010BFC8 /* Python.xcframework */, ++ 607A66142B0EFA380010BFC8 /* iOSTestbed */, ++ 607A66302B0EFA3A0010BFC8 /* TestbedTests */, + 607A66132B0EFA380010BFC8 /* Products */, + 607A664F2B0EFFE00010BFC8 /* Frameworks */, + ); @@ -2896,33 +2765,33 @@ index c3e261ecd9e..26ef7a95de4 100644 + 607A66132B0EFA380010BFC8 /* Products */ = { + isa = PBXGroup; + children = ( -+ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */, -+ 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */, ++ 607A66122B0EFA380010BFC8 /* iOSTestbed.app */, ++ 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; -+ 607A66142B0EFA380010BFC8 /* visionOSTestbed */ = { ++ 607A66142B0EFA380010BFC8 /* iOSTestbed */ = { + isa = PBXGroup; + children = ( + 608619552CB7819B00F46182 /* app */, + 608619532CB77BA900F46182 /* app_packages */, -+ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */, -+ 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */, ++ 607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */, + 607A66152B0EFA380010BFC8 /* AppDelegate.h */, + 607A66162B0EFA380010BFC8 /* AppDelegate.m */, + 607A66212B0EFA390010BFC8 /* Assets.xcassets */, ++ 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */, + 607A66272B0EFA390010BFC8 /* main.m */, + ); -+ path = visionOSTestbed; ++ path = iOSTestbed; + sourceTree = ""; + }; -+ 607A66302B0EFA3A0010BFC8 /* visionOSTestbedTests */ = { ++ 607A66302B0EFA3A0010BFC8 /* TestbedTests */ = { + isa = PBXGroup; + children = ( -+ 607A66312B0EFA3A0010BFC8 /* visionOSTestbedTests.m */, ++ 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */, + ); -+ path = visionOSTestbedTests; ++ path = TestbedTests; + sourceTree = ""; + }; + 607A664F2B0EFFE00010BFC8 /* Frameworks */ = { @@ -2935,29 +2804,28 @@ index c3e261ecd9e..26ef7a95de4 100644 +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ -+ 607A66112B0EFA380010BFC8 /* visionOSTestbed */ = { ++ 607A66112B0EFA380010BFC8 /* iOSTestbed */ = { + isa = PBXNativeTarget; -+ buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */; ++ buildConfigurationList = 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbed" */; + buildPhases = ( + 607A660E2B0EFA380010BFC8 /* Sources */, + 607A660F2B0EFA380010BFC8 /* Frameworks */, + 607A66102B0EFA380010BFC8 /* Resources */, -+ 607A66552B0F061D0010BFC8 /* Install Target Specific Python Standard Library */, -+ 607A66562B0F06200010BFC8 /* Prepare Python Binary Modules */, ++ 607A66552B0F061D0010BFC8 /* Process Python libraries */, + 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); -+ name = visionOSTestbed; ++ name = iOSTestbed; + productName = iOSTestbed; -+ productReference = 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */; ++ productReference = 607A66122B0EFA380010BFC8 /* iOSTestbed.app */; + productType = "com.apple.product-type.application"; + }; -+ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */ = { ++ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */ = { + isa = PBXNativeTarget; -+ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */; ++ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */; + buildPhases = ( + 607A66292B0EFA3A0010BFC8 /* Sources */, + 607A662A2B0EFA3A0010BFC8 /* Frameworks */, @@ -2969,9 +2837,9 @@ index c3e261ecd9e..26ef7a95de4 100644 + dependencies = ( + 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, + ); -+ name = visionOSTestbedTests; ++ name = iOSTestbedTests; + productName = iOSTestbedTests; -+ productReference = 607A662D2B0EFA3A0010BFC8 /* visionOSTestbedTests.xctest */; ++ productReference = 607A662D2B0EFA3A0010BFC8 /* iOSTestbedTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ @@ -2992,7 +2860,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + }; + }; + }; -+ buildConfigurationList = 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */; ++ buildConfigurationList = 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "iOSTestbed" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; @@ -3005,8 +2873,8 @@ index c3e261ecd9e..26ef7a95de4 100644 + projectDirPath = ""; + projectRoot = ""; + targets = ( -+ 607A66112B0EFA380010BFC8 /* visionOSTestbed */, -+ 607A662C2B0EFA3A0010BFC8 /* visionOSTestbedTests */, ++ 607A66112B0EFA380010BFC8 /* iOSTestbed */, ++ 607A662C2B0EFA3A0010BFC8 /* iOSTestbedTests */, + ); + }; +/* End PBXProject section */ @@ -3016,7 +2884,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( -+ 607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */, ++ 607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */, + 608619562CB7819B00F46182 /* app in Resources */, + 607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */, + 608619542CB77BA900F46182 /* app_packages in Resources */, @@ -3033,27 +2901,7 @@ index c3e261ecd9e..26ef7a95de4 100644 +/* 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 */ = { ++ 607A66552B0F061D0010BFC8 /* Process Python libraries */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; @@ -3063,14 +2911,14 @@ index c3e261ecd9e..26ef7a95de4 100644 + ); + inputPaths = ( + ); -+ name = "Prepare Python Binary Modules"; ++ name = "Process Python libraries"; + 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"; ++ shellScript = "set -e\nsource $PROJECT_DIR/Python.xcframework/build/utils.sh\ninstall_python Python.xcframework app app_packages\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ @@ -3089,7 +2937,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( -+ 607A66322B0EFA3A0010BFC8 /* visionOSTestbedTests.m in Sources */, ++ 607A66322B0EFA3A0010BFC8 /* TestbedTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; @@ -3098,11 +2946,22 @@ index c3e261ecd9e..26ef7a95de4 100644 +/* Begin PBXTargetDependency section */ + 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; -+ target = 607A66112B0EFA380010BFC8 /* visionOSTestbed */; ++ target = 607A66112B0EFA380010BFC8 /* iOSTestbed */; + targetProxy = 607A662E2B0EFA3A0010BFC8 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + ++/* Begin PBXVariantGroup section */ ++ 607A66232B0EFA390010BFC8 /* LaunchScreen.storyboard */ = { ++ isa = PBXVariantGroup; ++ children = ( ++ 607A66242B0EFA390010BFC8 /* Base */, ++ ); ++ name = LaunchScreen.storyboard; ++ sourceTree = ""; ++ }; ++/* End PBXVariantGroup section */ ++ +/* Begin XCBuildConfiguration section */ + 607A663F2B0EFA3A0010BFC8 /* Debug */ = { + isa = XCBuildConfiguration; @@ -3156,12 +3015,12 @@ index c3e261ecd9e..26ef7a95de4 100644 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; -+ SDKROOT = xros; ++ SDKROOT = iphoneos; + }; + name = Debug; + }; @@ -3211,11 +3070,11 @@ index c3e261ecd9e..26ef7a95de4 100644 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; -+ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; -+ SDKROOT = xros; ++ SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; @@ -3231,26 +3090,22 @@ index c3e261ecd9e..26ef7a95de4 100644 + DEVELOPMENT_TEAM = ""; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; -+ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist"; ++ INFOPLIST_FILE = "iOSTestbed/iOSTestbed-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"; ++ IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 3.13.0a1; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; + 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; ++ TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; @@ -3266,26 +3121,22 @@ index c3e261ecd9e..26ef7a95de4 100644 + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; -+ INFOPLIST_FILE = "visionOSTestbed/visionOSTestbed-Info.plist"; ++ INFOPLIST_FILE = "iOSTestbed/iOSTestbed-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"; ++ IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 3.13.0a1; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbed; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbed; + 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; ++ TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; @@ -3299,17 +3150,13 @@ index c3e261ecd9e..26ef7a95de4 100644 + DEVELOPMENT_TEAM = 3HEZE76D99; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; -+ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbedTests; + 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"; ++ TARGETED_DEVICE_FAMILY = "1,2"; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed"; + }; + name = Debug; + }; @@ -3323,24 +3170,20 @@ index c3e261ecd9e..26ef7a95de4 100644 + DEVELOPMENT_TEAM = 3HEZE76D99; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = "\"$(BUILT_PRODUCTS_DIR)/Python.framework/Headers\""; -+ IPHONEOS_DEPLOYMENT_TARGET = 12.0; ++ IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MARKETING_VERSION = 1.0; -+ PRODUCT_BUNDLE_IDENTIFIER = org.python.visionOSTestbedTests; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.iOSTestbedTests; + 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"; ++ TARGETED_DEVICE_FAMILY = "1,2"; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/iOSTestbed"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ -+ 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "visionOSTestbed" */ = { ++ 607A660D2B0EFA380010BFC8 /* Build configuration list for PBXProject "iOSTestbed" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 607A663F2B0EFA3A0010BFC8 /* Debug */, @@ -3349,7 +3192,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; -+ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbed" */ = { ++ 607A66412B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbed" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 607A66422B0EFA3A0010BFC8 /* Debug */, @@ -3358,7 +3201,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; -+ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "visionOSTestbedTests" */ = { ++ 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "iOSTestbedTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 607A66452B0EFA3A0010BFC8 /* Debug */, @@ -3372,11 +3215,160 @@ index c3e261ecd9e..26ef7a95de4 100644 + rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; +} --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/AppDelegate.h ++++ b/Apple/testbed/iOSTestbed.xcodeproj/xcshareddata/xcschemes/iOSTestbed.xcscheme +@@ -0,0 +1,97 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +--- /dev/null ++++ b/Apple/testbed/iOSTestbed.xctestplan +@@ -0,0 +1,46 @@ ++{ ++ "configurations" : [ ++ { ++ "id" : "F5A95CE4-1ADE-4A6E-A0E1-CDBAE26DF0C5", ++ "name" : "Test Scheme Action", ++ "options" : { ++ ++ } ++ } ++ ], ++ "defaultOptions" : { ++ "commandLineArgumentEntries" : [ ++ { ++ "argument" : "test" ++ }, ++ { ++ "argument" : "-uall" ++ }, ++ { ++ "argument" : "--single-process" ++ }, ++ { ++ "argument" : "--rerun" ++ }, ++ { ++ "argument" : "-W" ++ } ++ ], ++ "targetForVariableExpansion" : { ++ "containerPath" : "container:iOSTestbed.xcodeproj", ++ "identifier" : "607A66112B0EFA380010BFC8", ++ "name" : "iOSTestbed" ++ } ++ }, ++ "testTargets" : [ ++ { ++ "parallelizable" : false, ++ "target" : { ++ "containerPath" : "container:iOSTestbed.xcodeproj", ++ "identifier" : "607A662C2B0EFA3A0010BFC8", ++ "name" : "iOSTestbedTests" ++ } ++ } ++ ], ++ "version" : 1 ++} +--- /dev/null ++++ b/Apple/testbed/iOSTestbed/AppDelegate.h @@ -0,0 +1,11 @@ +// +// AppDelegate.h -+// visionOSTestbed ++// iOSTestbed +// + +#import @@ -3386,11 +3378,11 @@ index c3e261ecd9e..26ef7a95de4 100644 + +@end --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/AppDelegate.m ++++ b/Apple/testbed/iOSTestbed/AppDelegate.m @@ -0,0 +1,19 @@ +// +// AppDelegate.m -+// visionOSTestbed ++// iOSTestbed +// + +#import "AppDelegate.h" @@ -3408,7 +3400,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + +@end --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json ++++ b/Apple/testbed/iOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ @@ -3422,7 +3414,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + } +} --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json ++++ b/Apple/testbed/iOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ @@ -3438,7 +3430,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + } +} --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/Assets.xcassets/Contents.json ++++ b/Apple/testbed/iOSTestbed/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { @@ -3447,80 +3439,40 @@ index c3e261ecd9e..26ef7a95de4 100644 + } +} --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/app/README ++++ b/Apple/testbed/iOSTestbed/Base.lproj/LaunchScreen.storyboard +@@ -0,0 +1,9 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ +--- /dev/null ++++ b/Apple/testbed/iOSTestbed/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. ++Framework form. + +When the test suite runs, this folder will be on the PYTHONPATH, and will be the +working directory for the test suite. --- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/app_packages/README ++++ b/Apple/testbed/iOSTestbed/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. ++Framework form. + +When the test suite runs, this folder will be on the PYTHONPATH. --- /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 -+ -+ -+ ---- /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); -+ } -+} ---- /dev/null -+++ b/visionOS/testbed/visionOSTestbed/visionOSTestbed-Info.plist -@@ -0,0 +1,56 @@ ++++ b/Apple/testbed/iOSTestbed/iOSTestbed-Info.plist +@@ -0,0 +1,52 @@ + + + @@ -3532,7 +3484,7 @@ index c3e261ecd9e..26ef7a95de4 100644 + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier -+ org.python.visionOSTestbed ++ org.python.iOSTestbed + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName @@ -3545,23 +3497,12 @@ index c3e261ecd9e..26ef7a95de4 100644 + ???? + CFBundleVersion + 1 -+ TestArgs -+ -+ test -+ -uall -+ --single-process -+ --rerun -+ -W -+ -+ UIApplicationSceneManifest -+ -+ UIApplicationSupportsMultipleScenes -+ -+ UISceneConfigurations -+ -+ ++ LSRequiresIPhoneOS ++ + UIRequiresFullScreen + ++ UILaunchStoryboardName ++ Launch Screen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait @@ -3575,421 +3516,4397 @@ index c3e261ecd9e..26ef7a95de4 100644 + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + ++ UIApplicationSceneManifest ++ ++ UIApplicationSupportsMultipleScenes ++ ++ UISceneConfigurations ++ ++ + + --- /dev/null -+++ b/visionOS/testbed/visionOSTestbedTests/visionOSTestbedTests.m -@@ -0,0 +1,162 @@ -+#import -+#import ++++ b/Apple/testbed/iOSTestbed/main.m +@@ -0,0 +1,16 @@ ++// ++// main.m ++// iOSTestbed ++// + -+@interface visionOSTestbedTests : XCTestCase ++#import ++#import "AppDelegate.h" + -+@end ++int main(int argc, char * argv[]) { ++ NSString * appDelegateClassName; ++ @autoreleasepool { ++ appDelegateClassName = NSStringFromClass([AppDelegate class]); + -+@implementation visionOSTestbedTests ++ return UIApplicationMain(argc, argv, nil, appDelegateClassName); ++ } ++} +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed.xcodeproj/project.pbxproj +@@ -0,0 +1,505 @@ ++// !$*UTF8*$! ++{ ++ archiveVersion = 1; ++ classes = { ++ }; ++ objectVersion = 77; ++ objects = { + ++/* Begin PBXBuildFile section */ ++ EE7C8A1E2DCD6FF3003206DB /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */; }; ++ EE7C8A1F2DCD70CD003206DB /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */; }; ++ EE7C8A202DCD70CD003206DB /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; ++/* End PBXBuildFile section */ + -+- (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; ++/* Begin PBXContainerItemProxy section */ ++ EE989E662DCD6E7A0036B268 /* PBXContainerItemProxy */ = { ++ isa = PBXContainerItemProxy; ++ containerPortal = EE989E462DCD6E780036B268 /* Project object */; ++ proxyType = 1; ++ remoteGlobalIDString = EE989E4D2DCD6E780036B268; ++ remoteInfo = tvOSTestbed; ++ }; ++/* End PBXContainerItemProxy section */ + -+ NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; ++/* Begin PBXCopyFilesBuildPhase section */ ++ EE7C8A212DCD70CD003206DB /* Embed Frameworks */ = { ++ isa = PBXCopyFilesBuildPhase; ++ buildActionMask = 2147483647; ++ dstPath = ""; ++ dstSubfolderSpec = 10; ++ files = ( ++ EE7C8A202DCD70CD003206DB /* Python.xcframework in Embed Frameworks */, ++ ); ++ name = "Embed Frameworks"; ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXCopyFilesBuildPhase section */ + -+ // 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); ++/* Begin PBXFileReference section */ ++ 6077B3802E82A4BE00E3D6A3 /* tvOSTestbed.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = tvOSTestbed.xctestplan; sourceTree = ""; }; ++ EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = ""; }; ++ EE989E4E2DCD6E780036B268 /* tvOSTestbed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tvOSTestbed.app; sourceTree = BUILT_PRODUCTS_DIR; }; ++ EE989E652DCD6E7A0036B268 /* TestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; ++/* End PBXFileReference section */ + -+ // 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]); ++/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ ++ 6077B37F2E81892A00E3D6A3 /* Exceptions for "tvOSTestbed" folder in "tvOSTestbed" target */ = { ++ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; ++ membershipExceptions = ( ++ "tvOSTestbed-Info.plist", ++ ); ++ target = EE989E4D2DCD6E780036B268 /* tvOSTestbed */; ++ }; ++/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + -+ // Start the test suite. Print a separator to differentiate Python startup logs from app logs -+ NSLog(@"---------------------------------------------------------------------------"); ++/* Begin PBXFileSystemSynchronizedRootGroup section */ ++ EE989E502DCD6E780036B268 /* tvOSTestbed */ = { ++ isa = PBXFileSystemSynchronizedRootGroup; ++ exceptions = ( ++ 6077B37F2E81892A00E3D6A3 /* Exceptions for "tvOSTestbed" folder in "tvOSTestbed" target */, ++ ); ++ explicitFolders = ( ++ app, ++ app_packages, ++ ); ++ path = tvOSTestbed; ++ sourceTree = ""; ++ }; ++ EE989E682DCD6E7A0036B268 /* TestbedTests */ = { ++ isa = PBXFileSystemSynchronizedRootGroup; ++ path = TestbedTests; ++ sourceTree = ""; ++ }; ++/* End PBXFileSystemSynchronizedRootGroup section */ + -+ exit_code = Py_RunMain(); -+ XCTAssertEqual(exit_code, 0, @"Test suite did not pass"); ++/* Begin PBXFrameworksBuildPhase section */ ++ EE989E4B2DCD6E780036B268 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ EE7C8A1F2DCD70CD003206DB /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ EE989E622DCD6E7A0036B268 /* Frameworks */ = { ++ isa = PBXFrameworksBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ EE7C8A1E2DCD6FF3003206DB /* Python.xcframework in Frameworks */, ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXFrameworksBuildPhase section */ + -+ NSLog(@"---------------------------------------------------------------------------"); ++/* Begin PBXGroup section */ ++ EE989E452DCD6E780036B268 = { ++ isa = PBXGroup; ++ children = ( ++ 6077B3802E82A4BE00E3D6A3 /* tvOSTestbed.xctestplan */, ++ EE7C8A1C2DCD6FF3003206DB /* Python.xcframework */, ++ EE989E502DCD6E780036B268 /* tvOSTestbed */, ++ EE989E682DCD6E7A0036B268 /* TestbedTests */, ++ EE989E4F2DCD6E780036B268 /* Products */, ++ ); ++ sourceTree = ""; ++ }; ++ EE989E4F2DCD6E780036B268 /* Products */ = { ++ isa = PBXGroup; ++ children = ( ++ EE989E4E2DCD6E780036B268 /* tvOSTestbed.app */, ++ EE989E652DCD6E7A0036B268 /* TestbedTests.xctest */, ++ ); ++ name = Products; ++ sourceTree = ""; ++ }; ++/* End PBXGroup section */ + -+ Py_Finalize(); -+} ++/* Begin PBXNativeTarget section */ ++ EE989E4D2DCD6E780036B268 /* tvOSTestbed */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = EE989E792DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "tvOSTestbed" */; ++ buildPhases = ( ++ EE989E4A2DCD6E780036B268 /* Sources */, ++ EE989E4B2DCD6E780036B268 /* Frameworks */, ++ EE989E4C2DCD6E780036B268 /* Resources */, ++ EE7C8A222DCD70F4003206DB /* Process Python libraries */, ++ EE7C8A212DCD70CD003206DB /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ ); ++ fileSystemSynchronizedGroups = ( ++ EE989E502DCD6E780036B268 /* tvOSTestbed */, ++ ); ++ name = tvOSTestbed; ++ packageProductDependencies = ( ++ ); ++ productName = tvOSTestbed; ++ productReference = EE989E4E2DCD6E780036B268 /* tvOSTestbed.app */; ++ productType = "com.apple.product-type.application"; ++ }; ++ EE989E642DCD6E7A0036B268 /* TestbedTests */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = EE989E7C2DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "TestbedTests" */; ++ buildPhases = ( ++ EE989E612DCD6E7A0036B268 /* Sources */, ++ EE989E622DCD6E7A0036B268 /* Frameworks */, ++ EE989E632DCD6E7A0036B268 /* Resources */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ EE989E672DCD6E7A0036B268 /* PBXTargetDependency */, ++ ); ++ fileSystemSynchronizedGroups = ( ++ EE989E682DCD6E7A0036B268 /* TestbedTests */, ++ ); ++ name = TestbedTests; ++ packageProductDependencies = ( ++ ); ++ productName = TestbedTests; ++ productReference = EE989E652DCD6E7A0036B268 /* TestbedTests.xctest */; ++ productType = "com.apple.product-type.bundle.unit-test"; ++ }; ++/* End PBXNativeTarget section */ + ++/* Begin PBXProject section */ ++ EE989E462DCD6E780036B268 /* Project object */ = { ++ isa = PBXProject; ++ attributes = { ++ BuildIndependentTargetsInParallel = 1; ++ LastUpgradeCheck = 1620; ++ TargetAttributes = { ++ EE989E4D2DCD6E780036B268 = { ++ CreatedOnToolsVersion = 16.2; ++ }; ++ EE989E642DCD6E7A0036B268 = { ++ CreatedOnToolsVersion = 16.2; ++ TestTargetID = EE989E4D2DCD6E780036B268; ++ }; ++ }; ++ }; ++ buildConfigurationList = EE989E492DCD6E780036B268 /* Build configuration list for PBXProject "tvOSTestbed" */; ++ developmentRegion = en; ++ hasScannedForEncodings = 0; ++ knownRegions = ( ++ en, ++ Base, ++ ); ++ mainGroup = EE989E452DCD6E780036B268; ++ minimizedProjectReferenceProxies = 1; ++ preferredProjectObjectVersion = 77; ++ productRefGroup = EE989E4F2DCD6E780036B268 /* Products */; ++ projectDirPath = ""; ++ projectRoot = ""; ++ targets = ( ++ EE989E4D2DCD6E780036B268 /* tvOSTestbed */, ++ EE989E642DCD6E7A0036B268 /* TestbedTests */, ++ ); ++ }; ++/* End PBXProject section */ + -+@end ---- /dev/null -+++ b/watchOS/README.rst -@@ -0,0 +1,108 @@ -+======================== -+Python on watchOS README -+======================== ++/* Begin PBXResourcesBuildPhase section */ ++ EE989E4C2DCD6E780036B268 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ EE989E632DCD6E7A0036B268 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXResourcesBuildPhase section */ + -+:Authors: -+ Russell Keith-Magee (2023-11) ++/* Begin PBXShellScriptBuildPhase section */ ++ EE7C8A222DCD70F4003206DB /* Process Python libraries */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Process Python libraries"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\nsource $PROJECT_DIR/Python.xcframework/build/utils.sh\n\ninstall_python Python.xcframework app app_packages\n"; ++ }; ++/* End PBXShellScriptBuildPhase section */ + -+This document provides a quick overview of some watchOS specific features in the -+Python distribution. ++/* Begin PBXSourcesBuildPhase section */ ++ EE989E4A2DCD6E780036B268 /* Sources */ = { ++ isa = PBXSourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++ EE989E612DCD6E7A0036B268 /* Sources */ = { ++ isa = PBXSourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ }; ++/* End PBXSourcesBuildPhase section */ + -+Compilers for building on watchOS -+================================= ++/* Begin PBXTargetDependency section */ ++ EE989E672DCD6E7A0036B268 /* PBXTargetDependency */ = { ++ isa = PBXTargetDependency; ++ target = EE989E4D2DCD6E780036B268 /* tvOSTestbed */; ++ targetProxy = EE989E662DCD6E7A0036B268 /* PBXContainerItemProxy */; ++ }; ++/* End PBXTargetDependency section */ + -+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. ++/* Begin XCBuildConfiguration section */ ++ EE989E772DCD6E7A0036B268 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ALWAYS_SEARCH_USER_PATHS = NO; ++ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ++ CLANG_ANALYZER_NONNULL = YES; ++ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; ++ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; ++ CLANG_ENABLE_MODULES = YES; ++ CLANG_ENABLE_OBJC_ARC = YES; ++ CLANG_ENABLE_OBJC_WEAK = YES; ++ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; ++ CLANG_WARN_BOOL_CONVERSION = YES; ++ CLANG_WARN_COMMA = YES; ++ CLANG_WARN_CONSTANT_CONVERSION = YES; ++ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; ++ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; ++ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; ++ CLANG_WARN_EMPTY_BODY = YES; ++ CLANG_WARN_ENUM_CONVERSION = YES; ++ CLANG_WARN_INFINITE_RECURSION = YES; ++ CLANG_WARN_INT_CONVERSION = YES; ++ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; ++ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; ++ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; ++ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; ++ CLANG_WARN_STRICT_PROTOTYPES = YES; ++ CLANG_WARN_SUSPICIOUS_MOVE = YES; ++ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; ++ CLANG_WARN_UNREACHABLE_CODE = YES; ++ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ++ COPY_PHASE_STRIP = NO; ++ DEBUG_INFORMATION_FORMAT = dwarf; ++ ENABLE_STRICT_OBJC_MSGSEND = YES; ++ ENABLE_TESTABILITY = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)"; ++ GCC_C_LANGUAGE_STANDARD = gnu17; ++ GCC_DYNAMIC_NO_PIC = NO; ++ GCC_NO_COMMON_BLOCKS = YES; ++ GCC_OPTIMIZATION_LEVEL = 0; ++ GCC_PREPROCESSOR_DEFINITIONS = ( ++ "DEBUG=1", ++ "$(inherited)", ++ ); ++ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; ++ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; ++ GCC_WARN_UNDECLARED_SELECTOR = YES; ++ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; ++ GCC_WARN_UNUSED_FUNCTION = YES; ++ GCC_WARN_UNUSED_VARIABLE = YES; ++ HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"; ++ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; ++ MTL_FAST_MATH = YES; ++ ONLY_ACTIVE_ARCH = YES; ++ SDKROOT = appletvos; ++ TVOS_DEPLOYMENT_TARGET = 18.2; ++ }; ++ name = Debug; ++ }; ++ EE989E782DCD6E7A0036B268 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ALWAYS_SEARCH_USER_PATHS = NO; ++ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ++ CLANG_ANALYZER_NONNULL = YES; ++ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; ++ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; ++ CLANG_ENABLE_MODULES = YES; ++ CLANG_ENABLE_OBJC_ARC = YES; ++ CLANG_ENABLE_OBJC_WEAK = YES; ++ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; ++ CLANG_WARN_BOOL_CONVERSION = YES; ++ CLANG_WARN_COMMA = YES; ++ CLANG_WARN_CONSTANT_CONVERSION = YES; ++ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; ++ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; ++ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; ++ CLANG_WARN_EMPTY_BODY = YES; ++ CLANG_WARN_ENUM_CONVERSION = YES; ++ CLANG_WARN_INFINITE_RECURSION = YES; ++ CLANG_WARN_INT_CONVERSION = YES; ++ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; ++ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; ++ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; ++ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; ++ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; ++ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; ++ CLANG_WARN_STRICT_PROTOTYPES = YES; ++ CLANG_WARN_SUSPICIOUS_MOVE = YES; ++ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; ++ CLANG_WARN_UNREACHABLE_CODE = YES; ++ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ++ COPY_PHASE_STRIP = NO; ++ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ++ ENABLE_NS_ASSERTIONS = NO; ++ ENABLE_STRICT_OBJC_MSGSEND = YES; ++ ENABLE_TESTABILITY = YES; ++ ENABLE_USER_SCRIPT_SANDBOXING = NO; ++ FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)"; ++ GCC_C_LANGUAGE_STANDARD = gnu17; ++ GCC_NO_COMMON_BLOCKS = YES; ++ GCC_WARN_64_TO_32_BIT_CONVERSION = YES; ++ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; ++ GCC_WARN_UNDECLARED_SELECTOR = YES; ++ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; ++ GCC_WARN_UNUSED_FUNCTION = YES; ++ GCC_WARN_UNUSED_VARIABLE = YES; ++ HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/Python.framework/Headers"; ++ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; ++ MTL_ENABLE_DEBUG_INFO = NO; ++ MTL_FAST_MATH = YES; ++ SDKROOT = appletvos; ++ TVOS_DEPLOYMENT_TARGET = 18.2; ++ VALIDATE_PRODUCT = YES; ++ }; ++ name = Release; ++ }; ++ EE989E7A2DCD6E7A0036B268 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ++ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ GENERATE_INFOPLIST_FILE = NO; ++ INFOPLIST_FILE = "tvOSTestbed/tvOSTestbed-Info.plist"; ++ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; ++ INFOPLIST_KEY_UIMainStoryboardFile = Main; ++ INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.tvOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = 3; ++ }; ++ name = Debug; ++ }; ++ EE989E7B2DCD6E7A0036B268 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ++ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ GENERATE_INFOPLIST_FILE = NO; ++ INFOPLIST_FILE = "tvOSTestbed/tvOSTestbed-Info.plist"; ++ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; ++ INFOPLIST_KEY_UIMainStoryboardFile = Main; ++ INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; ++ LD_RUNPATH_SEARCH_PATHS = ( ++ "$(inherited)", ++ "@executable_path/Frameworks", ++ ); ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.tvOSTestbed; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = YES; ++ TARGETED_DEVICE_FAMILY = 3; ++ }; ++ name = Release; ++ }; ++ EE989E7D2DCD6E7A0036B268 /* Debug */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ BUNDLE_LOADER = "$(TEST_HOST)"; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ GENERATE_INFOPLIST_FILE = YES; ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.TestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = 3; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tvOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tvOSTestbed"; ++ TVOS_DEPLOYMENT_TARGET = 18.2; ++ }; ++ name = Debug; ++ }; ++ EE989E7E2DCD6E7A0036B268 /* Release */ = { ++ isa = XCBuildConfiguration; ++ buildSettings = { ++ BUNDLE_LOADER = "$(TEST_HOST)"; ++ CODE_SIGN_STYLE = Automatic; ++ CURRENT_PROJECT_VERSION = 1; ++ GENERATE_INFOPLIST_FILE = YES; ++ MARKETING_VERSION = 1.0; ++ PRODUCT_BUNDLE_IDENTIFIER = org.python.TestbedTests; ++ PRODUCT_NAME = "$(TARGET_NAME)"; ++ SWIFT_EMIT_LOC_STRINGS = NO; ++ TARGETED_DEVICE_FAMILY = 3; ++ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/tvOSTestbed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/tvOSTestbed"; ++ TVOS_DEPLOYMENT_TARGET = 18.2; ++ }; ++ name = Release; ++ }; ++/* End XCBuildConfiguration section */ + -+watchOS specific arguments to configure -+======================================= ++/* Begin XCConfigurationList section */ ++ EE989E492DCD6E780036B268 /* Build configuration list for PBXProject "tvOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ EE989E772DCD6E7A0036B268 /* Debug */, ++ EE989E782DCD6E7A0036B268 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ EE989E792DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "tvOSTestbed" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ EE989E7A2DCD6E7A0036B268 /* Debug */, ++ EE989E7B2DCD6E7A0036B268 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++ EE989E7C2DCD6E7A0036B268 /* Build configuration list for PBXNativeTarget "TestbedTests" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ EE989E7D2DCD6E7A0036B268 /* Debug */, ++ EE989E7E2DCD6E7A0036B268 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++/* End XCConfigurationList section */ ++ }; ++ rootObject = EE989E462DCD6E780036B268 /* Project object */; ++} +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed.xcodeproj/xcshareddata/xcschemes/tvOSTestbed.xcscheme +@@ -0,0 +1,97 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed.xctestplan +@@ -0,0 +1,46 @@ ++{ ++ "configurations" : [ ++ { ++ "id" : "F5A95CE4-1ADE-4A6E-A0E1-CDBAE26DF0C5", ++ "name" : "Test Scheme Action", ++ "options" : { + -+* ``--enable-framework[=DIR]`` ++ } ++ } ++ ], ++ "defaultOptions" : { ++ "commandLineArgumentEntries" : [ ++ { ++ "argument" : "test" ++ }, ++ { ++ "argument" : "-uall" ++ }, ++ { ++ "argument" : "--single-process" ++ }, ++ { ++ "argument" : "--rerun" ++ }, ++ { ++ "argument" : "-W" ++ } ++ ], ++ "targetForVariableExpansion" : { ++ "containerPath" : "container:tvOSTestbed.xcodeproj", ++ "identifier" : "607A66112B0EFA380010BFC8", ++ "name" : "tvOSTestbed" ++ } ++ }, ++ "testTargets" : [ ++ { ++ "parallelizable" : false, ++ "target" : { ++ "containerPath" : "container:tvOSTestbed.xcodeproj", ++ "identifier" : "EE989E642DCD6E7A0036B268", ++ "name" : "TestbedTests" ++ } ++ } ++ ], ++ "version" : 1 ++} +\ No newline at end of file +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed/AppDelegate.h +@@ -0,0 +1,11 @@ ++// ++// AppDelegate.h ++// tvOSTestbed ++// + -+ This argument specifies the location where the Python.framework will -+ be installed. ++#import + -+* ``--with-framework-name=NAME`` ++@interface AppDelegate : UIResponder + -+ Specify the name for the python framework, defaults to ``Python``. + ++@end +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed/AppDelegate.m +@@ -0,0 +1,19 @@ ++// ++// AppDelegate.m ++// tvOSTestbed ++// + -+Building and using Python on watchOS -+==================================== ++#import "AppDelegate.h" + -+ABIs and Architectures -+---------------------- ++@interface AppDelegate () + -+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. ++@end + -+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.) ++@implementation AppDelegate + -+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? -+------------------------------- ++- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ++ return YES; ++} + -+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: ++@end +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed/Base.lproj/LaunchScreen.storyboard +@@ -0,0 +1,24 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed/app/README +@@ -0,0 +1,7 @@ ++This folder can contain any Python application code. + -+* ``/path/to/install`` is the location where the final Python.framework will be -+ output. ++During the build, any binary modules found in this folder will be processed into ++Framework form. + -+* ``--host`` is the architecture and ABI that you want to build, in GNU compiler -+ triple format. This will be one of: ++When the test suite runs, this folder will be on the PYTHONPATH, and will be the ++working directory for the test suite. +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed/app_packages/README +@@ -0,0 +1,7 @@ ++This folder can be a target for installing any Python dependencies needed by the ++test suite. + -+ - ``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. ++During the build, any binary modules found in this folder will be processed into ++Framework form. + -+* ``--build`` is the GNU compiler triple for the machine that will be running -+ the compiler. This is one of: ++When the test suite runs, this folder will be on the PYTHONPATH. +--- /dev/null ++++ b/Apple/testbed/tvOSTestbed/main.m +@@ -0,0 +1,16 @@ ++// ++// main.m ++// tvOSTestbed ++// + -+ - ``aarch64-apple-darwin`` for Apple Silicon devices. -+ - ``x86_64-apple-darwin`` for Intel devices. ++#import ++#import "AppDelegate.h" + -+* ``/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. ++int main(int argc, char * argv[]) { ++ NSString * appDelegateClassName; ++ @autoreleasepool { ++ appDelegateClassName = NSStringFromClass([AppDelegate class]); + -+Using a framework-based Python on watchOS -+====================================== ++ return UIApplicationMain(argc, argv, nil, appDelegateClassName); ++ } ++} --- /dev/null -+++ b/watchOS/Resources/Info.plist.in -@@ -0,0 +1,34 @@ ++++ b/Apple/testbed/tvOSTestbed/tvOSTestbed-Info.plist +@@ -0,0 +1,52 @@ + -+ -+ ++ ++ + + CFBundleDevelopmentRegion + en ++ CFBundleDisplayName ++ ${PRODUCT_NAME} + CFBundleExecutable -+ Python -+ CFBundleGetInfoString -+ Python Runtime and Library ++ ${EXECUTABLE_NAME} + CFBundleIdentifier -+ @PYTHONFRAMEWORKIDENTIFIER@ ++ org.python.tvOSTestbed + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName -+ Python ++ ${PRODUCT_NAME} + CFBundlePackageType -+ FMWK ++ APPL + CFBundleShortVersionString -+ %VERSION% -+ CFBundleLongVersionString -+ %VERSION%, (c) 2001-2023 Python Software Foundation. ++ 1.0 + CFBundleSignature + ???? + CFBundleVersion -+ %VERSION% -+ CFBundleSupportedPlatforms ++ 1 ++ LSRequiresIPhoneOS ++ ++ UIRequiresFullScreen ++ ++ UILaunchStoryboardName ++ Launch Screen ++ UISupportedInterfaceOrientations + -+ watchOS ++ UIInterfaceOrientationPortrait ++ UIInterfaceOrientationLandscapeLeft ++ UIInterfaceOrientationLandscapeRight + -+ MinimumOSVersion -+ @WATCHOS_DEPLOYMENT_TARGET@ -+ -+ ---- /dev/null -+++ b/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 "$@" ---- /dev/null -+++ b/watchOS/Resources/bin/arm64_32-apple-watchos-ar -@@ -0,0 +1,2 @@ -+#!/bin/bash -+xcrun --sdk watchos${WATCHOS_SDK_VERSION} ar "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 "$@" ---- /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 ++ UISupportedInterfaceOrientations~ipad + -+ watchOS ++ UIInterfaceOrientationPortrait ++ UIInterfaceOrientationPortraitUpsideDown ++ UIInterfaceOrientationLandscapeLeft ++ UIInterfaceOrientationLandscapeRight + -+ MinimumOSVersion -+ 4.0 -+ CFBundleVersion -+ 1 ++ UIApplicationSceneManifest ++ ++ UIApplicationSupportsMultipleScenes ++ ++ UISceneConfigurations ++ ++ + + --- /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 ++++ b/Apple/testbed/visionOSTestbed.xcodeproj/project.pbxproj +@@ -0,0 +1,558 @@ ++// !$*UTF8*$! ++{ ++ archiveVersion = 1; ++ classes = { ++ }; ++ objectVersion = 56; ++ objects = { + -+#ifdef __x86_64__ -+#include "pyconfig-x86_64.h" -+#endif ++/* 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 /* TestbedTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */; }; ++ 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 */ ++ 6077B3D62E82E60C00E3D6A3 /* visionOSTestbed.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = visionOSTestbed.xctestplan; sourceTree = ""; }; ++ 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 /* TestbedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TestbedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; ++ 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestbedTests.m; 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 = ( ++ 6077B3D62E82E60C00E3D6A3 /* visionOSTestbed.xctestplan */, ++ EEE9C80C2DAB5ECA0056F8C6 /* Python.xcframework */, ++ 607A66142B0EFA380010BFC8 /* visionOSTestbed */, ++ 607A66302B0EFA3A0010BFC8 /* TestbedTests */, ++ 607A66132B0EFA380010BFC8 /* Products */, ++ 607A664F2B0EFFE00010BFC8 /* Frameworks */, ++ ); ++ sourceTree = ""; ++ }; ++ 607A66132B0EFA380010BFC8 /* Products */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */, ++ 607A662D2B0EFA3A0010BFC8 /* TestbedTests.xctest */, ++ ); ++ name = Products; ++ sourceTree = ""; ++ }; ++ 607A66142B0EFA380010BFC8 /* visionOSTestbed */ = { ++ isa = PBXGroup; ++ children = ( ++ 608619552CB7819B00F46182 /* app */, ++ 608619532CB77BA900F46182 /* app_packages */, ++ 607A66592B0F08600010BFC8 /* visionOSTestbed-Info.plist */, ++ 607A66152B0EFA380010BFC8 /* AppDelegate.h */, ++ 607A66162B0EFA380010BFC8 /* AppDelegate.m */, ++ 607A66212B0EFA390010BFC8 /* Assets.xcassets */, ++ 607A66272B0EFA390010BFC8 /* main.m */, ++ ); ++ path = visionOSTestbed; ++ sourceTree = ""; ++ }; ++ 607A66302B0EFA3A0010BFC8 /* TestbedTests */ = { ++ isa = PBXGroup; ++ children = ( ++ 607A66312B0EFA3A0010BFC8 /* TestbedTests.m */, ++ ); ++ path = TestbedTests; ++ 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 /* Process Python libraries */, ++ 607A664E2B0EFC080010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ ); ++ name = visionOSTestbed; ++ productName = iOSTestbed; ++ productReference = 607A66122B0EFA380010BFC8 /* visionOSTestbed.app */; ++ productType = "com.apple.product-type.application"; ++ }; ++ 607A662C2B0EFA3A0010BFC8 /* TestbedTests */ = { ++ isa = PBXNativeTarget; ++ buildConfigurationList = 607A66442B0EFA3A0010BFC8 /* Build configuration list for PBXNativeTarget "TestbedTests" */; ++ buildPhases = ( ++ 607A66292B0EFA3A0010BFC8 /* Sources */, ++ 607A662A2B0EFA3A0010BFC8 /* Frameworks */, ++ 607A662B2B0EFA3A0010BFC8 /* Resources */, ++ 607A66522B0EFFE00010BFC8 /* Embed Frameworks */, ++ ); ++ buildRules = ( ++ ); ++ dependencies = ( ++ 607A662F2B0EFA3A0010BFC8 /* PBXTargetDependency */, ++ ); ++ name = TestbedTests; ++ productName = iOSTestbedTests; ++ productReference = 607A662D2B0EFA3A0010BFC8 /* TestbedTests.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 /* TestbedTests */, ++ ); ++ }; ++/* End PBXProject section */ ++ ++/* Begin PBXResourcesBuildPhase section */ ++ 607A66102B0EFA380010BFC8 /* Resources */ = { ++ isa = PBXResourcesBuildPhase; ++ buildActionMask = 2147483647; ++ files = ( ++ 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 /* Process Python libraries */ = { ++ isa = PBXShellScriptBuildPhase; ++ alwaysOutOfDate = 1; ++ buildActionMask = 2147483647; ++ files = ( ++ ); ++ inputFileListPaths = ( ++ ); ++ inputPaths = ( ++ ); ++ name = "Process Python libraries"; ++ outputFileListPaths = ( ++ ); ++ outputPaths = ( ++ ); ++ runOnlyForDeploymentPostprocessing = 0; ++ shellPath = /bin/sh; ++ shellScript = "set -e\n\nsource $PROJECT_DIR/Python.xcframework/build/utils.sh\n\ninstall_python Python.xcframework app app_packages\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 /* TestbedTests.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.TestbedTests; ++ 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.TestbedTests; ++ 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 "TestbedTests" */ = { ++ isa = XCConfigurationList; ++ buildConfigurations = ( ++ 607A66452B0EFA3A0010BFC8 /* Debug */, ++ 607A66462B0EFA3A0010BFC8 /* Release */, ++ ); ++ defaultConfigurationIsVisible = 0; ++ defaultConfigurationName = Release; ++ }; ++/* End XCConfigurationList section */ ++ }; ++ rootObject = 607A660A2B0EFA380010BFC8 /* Project object */; ++} +--- /dev/null ++++ b/Apple/testbed/visionOSTestbed.xcodeproj/xcshareddata/xcschemes/visionOSTestbed.xcscheme +@@ -0,0 +1,97 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +--- /dev/null ++++ b/Apple/testbed/visionOSTestbed.xctestplan +@@ -0,0 +1,46 @@ ++{ ++ "configurations" : [ ++ { ++ "id" : "C17FA044-0B70-48CA-AFF8-BC252081002F", ++ "name" : "Test Scheme Action", ++ "options" : { ++ ++ } ++ } ++ ], ++ "defaultOptions" : { ++ "commandLineArgumentEntries" : [ ++ { ++ "argument" : "test" ++ }, ++ { ++ "argument" : "-uall" ++ }, ++ { ++ "argument" : "--single-process" ++ }, ++ { ++ "argument" : "--rerun" ++ }, ++ { ++ "argument" : "-W" ++ } ++ ], ++ "targetForVariableExpansion" : { ++ "containerPath" : "container:visionOSTestbed.xcodeproj", ++ "identifier" : "607A66112B0EFA380010BFC8", ++ "name" : "visionOSTestbed" ++ } ++ }, ++ "testTargets" : [ ++ { ++ "parallelizable" : false, ++ "target" : { ++ "containerPath" : "container:visionOSTestbed.xcodeproj", ++ "identifier" : "607A662C2B0EFA3A0010BFC8", ++ "name" : "TestbedTests" ++ } ++ } ++ ], ++ "version" : 1 ++} +\ No newline at end of file +--- /dev/null ++++ b/Apple/testbed/visionOSTestbed/AppDelegate.h +@@ -0,0 +1,11 @@ ++// ++// AppDelegate.h ++// visionOSTestbed ++// ++ ++#import ++ ++@interface AppDelegate : UIResponder ++ ++ ++@end +--- /dev/null ++++ b/Apple/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 +--- /dev/null ++++ b/Apple/testbed/visionOSTestbed/Assets.xcassets/AccentColor.colorset/Contents.json +@@ -0,0 +1,11 @@ ++{ ++ "colors" : [ ++ { ++ "idiom" : "universal" ++ } ++ ], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/Apple/testbed/visionOSTestbed/Assets.xcassets/AppIcon.appiconset/Contents.json +@@ -0,0 +1,13 @@ ++{ ++ "images" : [ ++ { ++ "idiom" : "universal", ++ "platform" : "ios", ++ "size" : "1024x1024" ++ } ++ ], ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/Apple/testbed/visionOSTestbed/Assets.xcassets/Contents.json +@@ -0,0 +1,6 @@ ++{ ++ "info" : { ++ "author" : "xcode", ++ "version" : 1 ++ } ++} +--- /dev/null ++++ b/Apple/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. +--- /dev/null ++++ b/Apple/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. +--- /dev/null ++++ b/Apple/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); ++ } ++} +--- /dev/null ++++ b/Apple/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 ++ ++ ++ +--- /dev/null ++++ b/Apple/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 ++====================================== +--- /dev/null ++++ b/Apple/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@ ++ ++ +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos${TVOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-clang +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos${TVOS_DEPLOYMENT_TARGET} "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-clang++ +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang++ -target arm64-apple-tvos${TVOS_DEPLOYMENT_TARGET} "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvos${TVOS_SDK_VERSION} clang -target arm64-apple-tvos${TVOS_DEPLOYMENT_TARGET} -E "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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${TVOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${TVOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${TVOS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-simulator-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} strip -arch arm64 "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/arm64-apple-tvos-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk iphoneos${TVOS_SDK_VERSION} strip -arch arm64 "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/x86_64-apple-tvos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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${TVOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${TVOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${TVOS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/bin/x86_64-apple-tvos-simulator-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk appletvsimulator${TVOS_SDK_VERSION} strip -arch x86_64 "$@" +--- /dev/null ++++ b/Apple/tvOS/Resources/pyconfig.h +@@ -0,0 +1,7 @@ ++#ifdef __arm64__ ++#include "pyconfig-arm64.h" ++#endif ++ ++#ifdef __x86_64__ ++#include "pyconfig-x86_64.h" ++#endif +--- /dev/null ++++ b/Apple/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@ ++ ++ +--- /dev/null ++++ b/Apple/visionOS/Resources/bin/arm64-apple-xros-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xros${XROS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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} "$@" +--- /dev/null ++++ b/Apple/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} "$@" +--- /dev/null ++++ b/Apple/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 "$@" +--- /dev/null ++++ b/Apple/visionOS/Resources/bin/arm64-apple-xros-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xrsimulator${XROS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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 "$@" +--- /dev/null ++++ b/Apple/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 "$@" +--- /dev/null ++++ b/Apple/visionOS/Resources/bin/arm64-apple-xros-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk xrsimulator${XROS_SDK_VERSION} clang -target arm64-apple-xros${XROS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/Apple/visionOS/Resources/bin/arm64-apple-xros-simulator-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk xrsimulator${XROS_SDK_VERSION} strip -arch arm64 "$@" +--- /dev/null ++++ b/Apple/visionOS/Resources/bin/arm64-apple-xros-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk xros${XROS_SDK_VERSION} strip -arch arm64 "$@" +--- /dev/null ++++ b/Apple/visionOS/Resources/pyconfig.h +@@ -0,0 +1,3 @@ ++#ifdef __arm64__ ++#include "pyconfig-arm64.h" ++#endif +--- /dev/null ++++ b/Apple/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 ++====================================== +--- /dev/null ++++ b/Apple/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@ ++ ++ +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/arm64-apple-watchos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/arm64-apple-watchos-simulator-cpp +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator clang -target arm64-apple-watchos${WATCHOS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/arm64-apple-watchos-simulator-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} strip -arch arm64 "$@" +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/arm64-apple-watchos-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk watchos${WATCHOS_SDK_VERSION} strip -arch arm64 "$@" +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/arm64_32-apple-watchos-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchos${WATCHOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET} "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET} "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET} -E "$@" +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/x86_64-apple-watchos-simulator-ar +@@ -0,0 +1,2 @@ ++#!/bin/bash ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} ar "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET}-simulator "$@" +--- /dev/null ++++ b/Apple/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${WATCHOS_DEPLOYMENT_TARGET}-simulator -E "$@" +--- /dev/null ++++ b/Apple/watchOS/Resources/bin/x86_64-apple-watchos-simulator-strip +@@ -0,0 +1,2 @@ ++#!/bin/sh ++xcrun --sdk watchsimulator${WATCHOS_SDK_VERSION} strip -arch x86_64 "$@" +--- /dev/null ++++ b/Apple/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 +diff --git a/Doc/using/ios.rst b/Doc/using/ios.rst +index 91cfed16f0e..5e4033fb6ce 100644 +--- a/Doc/using/ios.rst ++++ b/Doc/using/ios.rst +@@ -170,7 +170,7 @@ + To add Python to an iOS Xcode project: + + 1. Build or obtain a Python ``XCFramework``. See the instructions in +- :source:`iOS/README.rst` (in the CPython source distribution) for details on ++ :source:`Apple/iOS/README.md` (in the CPython source distribution) for details on + how to build a Python ``XCFramework``. At a minimum, you will need a build + that supports ``arm64-apple-ios``, plus one of either + ``arm64-apple-ios-simulator`` or ``x86_64-apple-ios-simulator``. +@@ -180,22 +180,19 @@ + of your project; however, you can use any other location that you want by + adjusting paths as needed. + +-3. Drag the ``iOS/Resources/dylib-Info-template.plist`` file into your project, +- and ensure it is associated with the app target. +- +-4. Add your application code as a folder in your Xcode project. In the ++3. Add your application code as a folder in your Xcode project. In the + following instructions, we'll assume that your user code is in a folder + named ``app`` in the root of your project; you can use any other location by + adjusting paths as needed. Ensure that this folder is associated with your + app target. + +-5. Select the app target by selecting the root node of your Xcode project, then ++4. Select the app target by selecting the root node of your Xcode project, then + the target name in the sidebar that appears. + +-6. In the "General" settings, under "Frameworks, Libraries and Embedded ++5. In the "General" settings, under "Frameworks, Libraries and Embedded + Content", add ``Python.xcframework``, with "Embed & Sign" selected. + +-7. In the "Build Settings" tab, modify the following: ++6. In the "Build Settings" tab, modify the following: + + - Build Options + +@@ -211,86 +208,24 @@ + + * Quoted Include In Framework Header: No + +-8. Add a build step that copies the Python standard library into your app. In +- the "Build Phases" tab, add a new "Run Script" build step *before* the +- "Embed Frameworks" step, but *after* the "Copy Bundle Resources" step. Name +- the step "Install Target Specific Python Standard Library", disable the +- "Based on dependency analysis" checkbox, and set the script content to: ++7. Add a build step that processes the Python standard library, and your own ++ Python binary dependencies. In the "Build Phases" tab, add a new "Run ++ Script" build step *before* the "Embed Frameworks" step, but *after* the ++ "Copy Bundle Resources" step. Name the step "Process Python libraries", ++ disable the "Based on dependency analysis" checkbox, and set the script ++ content to: + + .. code-block:: bash + +- set -e +- +- mkdir -p "$CODESIGNING_FOLDER_PATH/python/lib" +- if [ "$EFFECTIVE_PLATFORM_NAME" = "-iphonesimulator" ]; then +- echo "Installing Python modules for iOS Simulator" +- rsync -au --delete "$PROJECT_DIR/Python.xcframework/ios-arm64_x86_64-simulator/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" +- else +- echo "Installing Python modules for iOS Device" +- rsync -au --delete "$PROJECT_DIR/Python.xcframework/ios-arm64/lib/" "$CODESIGNING_FOLDER_PATH/python/lib/" +- fi ++ set -e ++ source $PROJECT_DIR/Python.xcframework/build/build_utils.sh ++ install_python Python.xcframework app + +- Note that the name of the simulator "slice" in the XCframework may be +- different, depending the CPU architectures your ``XCFramework`` supports. ++ If you have placed your XCframework somewhere other than the root of your ++ project, modify the path to the first argument. + +-9. Add a second build step that processes the binary extension modules in the +- standard library into "Framework" format. Add a "Run Script" build step +- *directly after* the one you added in step 8, named "Prepare Python Binary +- Modules". It should also have "Based on dependency analysis" unchecked, with +- the following script content: +- +- .. code-block:: bash +- +- set -e +- +- install_dylib () { +- INSTALL_BASE=$1 +- FULL_EXT=$2 +- +- # The name of the extension file +- EXT=$(basename "$FULL_EXT") +- # The location of the extension file, relative to the bundle +- RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} +- # The path to the extension file, relative to the install base +- PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/} +- # The full dotted name of the extension module, constructed from the file path. +- FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d "." -f 1 | tr "/" "."); +- # A bundle identifier; not actually used, but required by Xcode framework packaging +- FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr "_" "-") +- # The name of the framework folder. +- FRAMEWORK_FOLDER="Frameworks/$FULL_MODULE_NAME.framework" +- +- # If the framework folder doesn't exist, create it. +- if [ ! -d "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" ]; then +- echo "Creating framework for $RELATIVE_EXT" +- mkdir -p "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER" +- cp "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" +- plutil -replace CFBundleExecutable -string "$FULL_MODULE_NAME" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" +- plutil -replace CFBundleIdentifier -string "$FRAMEWORK_BUNDLE_ID" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist" +- fi +- +- echo "Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME" +- mv "$FULL_EXT" "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" +- # Create a placeholder .fwork file where the .so was +- echo "$FRAMEWORK_FOLDER/$FULL_MODULE_NAME" > ${FULL_EXT%.so}.fwork +- # Create a back reference to the .so file location in the framework +- echo "${RELATIVE_EXT%.so}.fwork" > "$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin" +- } +- +- PYTHON_VER=$(ls -1 "$CODESIGNING_FOLDER_PATH/python/lib") +- echo "Install Python $PYTHON_VER standard library extension modules..." +- find "$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload" -name "*.so" | while read FULL_EXT; do +- install_dylib python/lib/$PYTHON_VER/lib-dynload/ "$FULL_EXT" +- done +- +- # Clean up dylib template +- rm -f "$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist" +- +- echo "Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)..." +- 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 "{}" \; +- +-10. Add Objective C code to initialize and use a Python interpreter in embedded +- mode. You should ensure that: ++8. Add Objective C code to initialize and use a Python interpreter in embedded ++ mode. You should ensure that: + + * UTF-8 mode (:c:member:`PyPreConfig.utf8_mode`) is *enabled*; + * Buffered stdio (:c:member:`PyConfig.buffered_stdio`) is *disabled*; +@@ -309,22 +244,19 @@ + Your app's bundle location can be determined using ``[[NSBundle mainBundle] + resourcePath]``. + +-Steps 8, 9 and 10 of these instructions assume that you have a single folder of ++Steps 7 and 8 of these instructions assume that you have a single folder of + pure Python application code, named ``app``. If you have third-party binary + modules in your app, some additional steps will be required: + + * You need to ensure that any folders containing third-party binaries are +- either associated with the app target, or copied in as part of step 8. Step 8 +- should also purge any binaries that are not appropriate for the platform a +- specific build is targeting (i.e., delete any device binaries if you're +- building an app targeting the simulator). +- +-* Any folders that contain third-party binaries must be processed into +- framework form by step 9. The invocation of ``install_dylib`` that processes +- the ``lib-dynload`` folder can be copied and adapted for this purpose. ++ either associated with the app target, or are explicitly copied as part of ++ step 7. Step 7 should also purge any binaries that are not appropriate for ++ the platform a specific build is targeting (i.e., delete any device binaries ++ if you're building an app targeting the simulator). + +-* If you're using a separate folder for third-party packages, ensure that folder +- is included as part of the :envvar:`PYTHONPATH` configuration in step 10. ++* If you're using a separate folder for third-party packages, ensure that ++ folder is added to the end of the call to ``install_python`` in step 7, and ++ as part of the :envvar:`PYTHONPATH` configuration in step 8. + + * If any of the folders that contain third-party packages will contain ``.pth`` + files, you should add that folder as a *site directory* (using +@@ -334,25 +266,30 @@ + Testing a Python package + ------------------------ + +-The CPython source tree contains :source:`a testbed project ` that ++The CPython source tree contains :source:`a testbed project ` that + is used to run the CPython test suite on the iOS simulator. This testbed can also + be used as a testbed project for running your Python library's test suite on iOS. + +-After building or obtaining an iOS XCFramework (See :source:`iOS/README.rst` +-for details), create a clone of the Python iOS testbed project by running: ++After building or obtaining an iOS XCFramework (see :source:`Apple/iOS/README.md` ++for details), create a clone of the Python iOS testbed project. If you used the ++``Apple`` build script to build the XCframework, you can run: ++ ++.. code-block:: bash ++ ++ $ python cross-build/iOS/testbed clone --app --app app-testbed ++ ++Or, if you've sourced your own XCframework, by running: + + .. code-block:: bash + +- $ python iOS/testbed clone --framework --app --app app-testbed ++ $ python Apple/testbed clone --platform iOS --framework --app --app app-testbed + +-You will need to modify the ``iOS/testbed`` reference to point to that +-directory in the CPython source tree; any folders specified with the ``--app`` +-flag will be copied into the cloned testbed project. The resulting testbed will +-be created in the ``app-testbed`` folder. In this example, the ``module1`` and +-``module2`` would be importable modules at runtime. If your project has +-additional dependencies, they can be installed into the +-``app-testbed/iOSTestbed/app_packages`` folder (using ``pip install --target +-app-testbed/iOSTestbed/app_packages`` or similar). ++Any folders specified with the ``--app`` flag will be copied into the cloned ++testbed project. The resulting testbed will be created in the ``app-testbed`` ++folder. In this example, the ``module1`` and ``module2`` would be importable ++modules at runtime. If your project has additional dependencies, they can be ++installed into the ``app-testbed/Testbed/app_packages`` folder (using ``pip ++install --target app-testbed/Testbed/app_packages`` or similar). + + You can then use the ``app-testbed`` folder to run the test suite for your app, + For example, if ``module1.tests`` was the entry point to your test suite, you +@@ -381,7 +318,7 @@ + arguments. + + The test plan also disables parallel testing, and specifies the use of the +-``iOSTestbed.lldbinit`` file for providing configuration of the debugger. The ++``Testbed.lldbinit`` file for providing configuration of the debugger. The + default debugger configuration disables automatic breakpoints on the + ``SIGINT``, ``SIGUSR1``, ``SIGUSR2``, and ``SIGXFSZ`` signals. + +@@ -391,7 +328,12 @@ + The only mechanism for distributing apps to third-party iOS devices is to + submit the app to the iOS App Store; apps submitted for distribution must pass + Apple's app review process. This process includes a set of automated validation +-rules that inspect the submitted application bundle for problematic code. ++rules that inspect the submitted application bundle for problematic code. There ++are some steps that must be taken to ensure that your app will be able to pass ++these validation steps. ++ ++Incompatible code in the standard library ++----------------------------------------- + + The Python standard library contains some code that is known to violate these + automated rules. While these violations appear to be false positives, Apple's +@@ -402,3 +344,18 @@ + :source:`a patch file ` that will remove + all code that is known to cause issues with the App Store review process. This + patch is applied automatically when building for iOS. ++ ++Privacy manifests ++----------------- ++ ++In April 2025, Apple introduced a requirement for `certain third-party ++libraries to provide a Privacy Manifest ++`__. ++As a result, if you have a binary module that uses one of the affected ++libraries, you must provide an ``.xcprivacy`` file for that library. ++OpenSSL is one library affected by this requirement, but there are others. ++ ++If you produce a binary module named ``mymodule.so``, and use you the Xcode ++build script described in step 7 above, you can place a ``mymodule.xcprivacy`` ++file next to ``mymodule.so``, and the privacy manifest will be installed into ++the required location when the binary module is converted into a framework. +diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py +index 823a3692fd1..69550af2087 100644 +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -419,9 +419,9 @@ + if name: + name = _os.fspath(name) + +- # If the filename that has been provided is an iOS/tvOS/watchOS +- # .fwork file, dereference the location to the true origin of the +- # binary. ++ # If the filename that has been provided is an iOS, tvOS, visionOS ++ # or watchOS .fwork file, dereference the location to the true ++ # origin of the binary. + if name.endswith(".fwork"): + with open(name) as f: + name = _os.path.join( +diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py +index 378f12167c6..591c69adfb7 100644 +--- a/Lib/ctypes/util.py ++++ b/Lib/ctypes/util.py +@@ -126,7 +126,7 @@ + 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", "visionos", "watchos"}: + from ctypes.macholib.dyld import dyld_find as _dyld_find + def find_library(name): + possible = ['lib%s.dylib' % name, +@@ -444,7 +444,7 @@ + # 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 8bcd741c446..db11cbe0945 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', 'visionos', 'watchos' + _CASE_INSENSITIVE_PLATFORMS = (_CASE_INSENSITIVE_PLATFORMS_BYTES_KEY + + _CASE_INSENSITIVE_PLATFORMS_STR_KEY) + +@@ -1535,7 +1535,7 @@ + """ + extension_loaders = [] + if hasattr(_imp, 'create_dynamic'): +- if sys.platform in {"ios", "tvos", "watchos"}: ++ if sys.platform in {"ios", "tvos", "visionos", "watchos"}: + extension_loaders = [(AppleFrameworkLoader, [ + suffix.replace(".so", ".fwork") + for suffix in _imp.extension_suffixes() +diff --git a/Lib/platform.py b/Lib/platform.py +index 86141f072d2..5c77e30d8b5 100644 +--- a/Lib/platform.py ++++ b/Lib/platform.py +@@ -534,6 +534,78 @@ + return IOSVersionInfo(system, release, model, is_simulator) + + ++# A namedtuple for tvOS version information. ++TVOSVersionInfo = collections.namedtuple( ++ "TVOSVersionInfo", ++ ["system", "release", "model", "is_simulator"] ++) ++ ++ ++def tvos_ver(system="", release="", model="", is_simulator=False): ++ """Get tvOS version information, and return it as a namedtuple: ++ (system, release, model, is_simulator). ++ ++ If values can't be determined, they are set to values provided as ++ parameters. ++ """ ++ if sys.platform == "tvos": ++ # TODO: Can the iOS implementation be used here? ++ import _ios_support ++ result = _ios_support.get_platform_ios() ++ if result is not None: ++ return TVOSVersionInfo(*result) ++ ++ return TVOSVersionInfo(system, release, model, is_simulator) ++ ++ ++# A namedtuple for watchOS version information. ++WatchOSVersionInfo = collections.namedtuple( ++ "WatchOSVersionInfo", ++ ["system", "release", "model", "is_simulator"] ++) ++ ++ ++def watchos_ver(system="", release="", model="", is_simulator=False): ++ """Get watchOS version information, and return it as a namedtuple: ++ (system, release, model, is_simulator). ++ ++ If values can't be determined, they are set to values provided as ++ parameters. ++ """ ++ if sys.platform == "watchos": ++ # TODO: Can the iOS implementation be used here? ++ import _ios_support ++ result = _ios_support.get_platform_ios() ++ if result is not None: ++ return WatchOSVersionInfo(*result) ++ ++ return WatchOSVersionInfo(system, release, model, is_simulator) ++ ++ ++# A namedtuple for visionOS version information. ++VisionOSVersionInfo = collections.namedtuple( ++ "VisionOSVersionInfo", ++ ["system", "release", "model", "is_simulator"] ++) ++ ++ ++def visionos_ver(system="", release="", model="", is_simulator=False): ++ """Get visionOS version information, and return it as a namedtuple: ++ (system, release, model, is_simulator). ++ ++ If values can't be determined, they are set to values provided as ++ parameters. ++ """ ++ if sys.platform == "visionos": ++ # TODO: Can the iOS implementation be used here? ++ import _ios_support ++ result = _ios_support.get_platform_ios() ++ if result is not None: ++ return VisionOSVersionInfo(*result) ++ ++ return VisionOSVersionInfo(system, release, model, is_simulator) ++ ++ + def _java_getprop(name, default): + """This private helper is deprecated in 3.13 and will be removed in 3.15""" + from java.lang import System +@@ -733,7 +805,7 @@ + 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', 'visionos', 'watchos'}: + # XXX Others too ? + return default + +@@ -897,14 +969,30 @@ + 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/visionOS/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_visionos(): ++ 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` +@@ -1064,9 +1152,15 @@ + 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 == 'visionos': ++ system, release, _, _ = visionos_ver() ++ if sys.platform == 'watchos': ++ system, release, _, _ = watchos_ver() + + vals = system, node, release, version, machine + # Replace 'unknown' values with the more portable '' +@@ -1356,6 +1450,12 @@ + # 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 == "visionos": ++ system, release, _, _ = visionos_ver() ++ elif sys.platform == "watchos": ++ system, release, _, _ = watchos_ver() + else: + macos_release = mac_ver()[0] + if macos_release: +diff --git a/Lib/site.py b/Lib/site.py +index f9327197159..74899abecb0 100644 +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -298,8 +298,8 @@ + 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 54c2eb515b6..e02063aefea 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", "visionos", "watchos"} + + if _mswindows: + import _winapi +diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py +index f93b98dd681..07ec7853f85 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 @@ + # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories. + # Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling. + system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0] +- if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}: ++ if system_name in {"emscripten", "ios", "tvos", "visionos", "vxworks", "wasi", "watchos"}: + return None + + def joinuser(*args): +@@ -730,6 +733,18 @@ + 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", "12.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ elif sys.platform == "watchos": ++ release = get_config_vars().get("WATCHOS_DEPLOYMENT_TARGET", "4.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch ++ elif sys.platform == "visionos": ++ release = get_config_vars().get("XROS_DEPLOYMENT_TARGET", "2.0") ++ osname = sys.platform ++ machine = sys.implementation._multiarch + else: + import _osx_support + osname, release, machine = _osx_support.get_platform_osx( +diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py +index 1c1cbd03d02..1378985de4a 100644 +--- a/Lib/test/datetimetester.py ++++ b/Lib/test/datetimetester.py +@@ -7159,9 +7159,9 @@ + 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 a719e49ef37..c0cacd2a8d5 100644 +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -563,7 +563,7 @@ + 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", "visionos", "watchos"}: + unix_shell = '/system/bin/sh' if is_android else '/bin/sh' + else: + unix_shell = None +@@ -582,7 +582,7 @@ + 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", "visionos", "watchos"} + is_apple = is_apple_mobile or sys.platform == "darwin" + + has_fork_support = hasattr(os, "fork") and not ( +diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py +index a32d5d81d2b..f9421619e98 100644 +--- a/Lib/test/test__interpreters.py ++++ b/Lib/test/test__interpreters.py +@@ -612,6 +612,7 @@ + f'assert(obj == {obj!r})', + ) + ++ @support.requires_subprocess() + def test_os_exec(self): + expected = 'spam spam spam spam spam' + subinterp = _interpreters.create() +diff --git a/Lib/test/test_ctypes/test_dllist.py b/Lib/test/test_ctypes/test_dllist.py +index 15603dc3d77..bff6c0fb95f 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 6224c989e65..c5ccf225662 100644 +--- a/Lib/test/test_platform.py ++++ b/Lib/test/test_platform.py +@@ -271,13 +271,21 @@ + 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 4c3ea1cd8df..04a210e5c86 100644 +--- a/Lib/test/test_webbrowser.py ++++ b/Lib/test/test_webbrowser.py +@@ -236,7 +236,8 @@ + 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 f2e2394089d..2efbbfb0014 100644 +--- a/Lib/webbrowser.py ++++ b/Lib/webbrowser.py +@@ -488,7 +488,8 @@ + # 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 @@ + 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 764eef5be3e..19a332ffdcb 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -209,6 +209,12 @@ + # 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 + +@@ -2315,7 +2321,7 @@ + fi + + # Clone the testbed project into the XCFOLDER +- $(PYTHON_FOR_BUILD) $(srcdir)/iOS/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" ++ $(PYTHON_FOR_BUILD) $(srcdir)/Apple/testbed clone --framework $(PYTHONFRAMEWORKPREFIX) "$(XCFOLDER)" + + # Run the testbed project + $(PYTHON_FOR_BUILD) "$(XCFOLDER)" run --verbose -- test -uall --single-process --rerun -W +@@ -3219,10 +3225,10 @@ + -find build -type f -a ! -name '*.gc??' -exec rm -f {} ';' + -rm -f Include/pydtrace_probes.h + -rm -f profile-gen-stamp +- -rm -rf iOS/testbed/Python.xcframework/ios-*/bin +- -rm -rf iOS/testbed/Python.xcframework/ios-*/lib +- -rm -rf iOS/testbed/Python.xcframework/ios-*/include +- -rm -rf iOS/testbed/Python.xcframework/ios-*/Python.framework ++ -rm -rf Apple/iOS/testbed/Python.xcframework/ios-*/bin ++ -rm -rf Apple/iOS/testbed/Python.xcframework/ios-*/lib ++ -rm -rf Apple/iOS/testbed/Python.xcframework/ios-*/include ++ -rm -rf Apple/iOS/testbed/Python.xcframework/ios-*/Python.framework + + .PHONY: profile-removal + profile-removal: +@@ -3248,7 +3254,7 @@ + config.cache config.log pyconfig.h Modules/config.c + -rm -rf build platform + -rm -rf $(PYTHONFRAMEWORKDIR) +- -rm -rf iOS/Frameworks ++ -rm -rf Apple/iOS/Frameworks + -rm -rf iOSTestbed.* + -rm -f python-config.py python-config + -rm -rf cross-build +diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c +index f5cd73bdea8..6c1863c943b 100644 +--- a/Misc/platform_triplet.c ++++ b/Misc/platform_triplet.c +@@ -257,6 +257,32 @@ + # else + PLATFORM_TRIPLET=arm64-iphoneos + # endif ++# elif defined(TARGET_OS_TV) && TARGET_OS_TV ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR ++# if __x86_64__ ++PLATFORM_TRIPLET=x86_64-appletvsimulator ++# else ++PLATFORM_TRIPLET=arm64-appletvsimulator ++# endif ++# else ++PLATFORM_TRIPLET=arm64-appletvos ++# endif ++# elif defined(TARGET_OS_WATCH) && TARGET_OS_WATCH ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR ++# if __x86_64__ ++PLATFORM_TRIPLET=x86_64-watchsimulator ++# else ++PLATFORM_TRIPLET=arm64-watchsimulator ++# endif ++# else ++PLATFORM_TRIPLET=arm64_32-watchos ++# endif ++# elif defined(TARGET_OS_VISION) && TARGET_OS_VISION ++# if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR ++PLATFORM_TRIPLET=arm64-xrsimulator ++# else ++PLATFORM_TRIPLET=arm64-xros ++# endif + // Older macOS SDKs do not define TARGET_OS_OSX + # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX + PLATFORM_TRIPLET=darwin +diff --git a/config.sub b/config.sub +index 1bb6a05dc11..49febd56a37 100755 +--- a/config.sub ++++ b/config.sub +@@ -1743,7 +1743,7 @@ + | 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 @@ + ;; + *-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 6383271b477..b177687b0bc 100755 +--- a/configure ++++ b/configure +@@ -982,6 +982,10 @@ + CFLAGS + CC + HAS_XCRUN ++EXPORT_XROS_DEPLOYMENT_TARGET ++WATCHOS_DEPLOYMENT_TARGET ++XROS_DEPLOYMENT_TARGET ++TVOS_DEPLOYMENT_TARGET + IPHONEOS_DEPLOYMENT_TARGET + EXPORT_MACOSX_DEPLOYMENT_TARGET + CONFIGURE_MACOSX_DEPLOYMENT_TARGET +@@ -4116,6 +4120,15 @@ + *-apple-ios*) + ac_sys_system=iOS + ;; ++ *-apple-tvos*) ++ ac_sys_system=tvOS ++ ;; ++ *-apple-xros*) ++ ac_sys_system=visionOS ++ ;; ++ *-apple-watchos*) ++ ac_sys_system=watchOS ++ ;; + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -4197,7 +4210,7 @@ + # 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/visionOS/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 +4225,17 @@ + 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-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; ++ aarch64-apple-xros*) AR=arm64-apple-xros-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 +4244,17 @@ + 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-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;; ++ aarch64-apple-xros*) CC=arm64-apple-xros-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 +4263,17 @@ + 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-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; ++ aarch64-apple-xros*) CPP=arm64-apple-xros-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 +4282,17 @@ + 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-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;; ++ aarch64-apple-xros*) CXX=arm64-apple-xros-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 +4415,11 @@ + case $enableval in + yes) + case $ac_sys_system in +- Darwin) enableval=/Library/Frameworks ;; +- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ Darwin) enableval=/Library/Frameworks ;; ++ iOS) enableval=Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; ++ tvOS) enableval=Apple/tvOS/Frameworks/\$\(MULTIARCH\) ;; ++ visionOS) enableval=Apple/visionOS/Frameworks/\$\(MULTIARCH\) ;; ++ watchOS) enableval=Apple/watchOS/Frameworks/\$\(MULTIARCH\) ;; + *) as_fn_error $? "Unknown platform for framework build" "$LINENO" 5 + esac + esac +@@ -4368,6 +4428,9 @@ + 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 ;; ++ visionOS) as_fn_error $? "visionOS builds must use --enable-framework" "$LINENO" 5 ;; ++ watchOS) as_fn_error $? "watchOS builds must use --enable-framework" "$LINENO" 5 ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -4470,9 +4533,54 @@ + + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" +- RESSRCDIR=iOS/Resources ++ RESSRCDIR=Apple/iOS/Resources ++ ++ ac_config_files="$ac_config_files Apple/iOS/Resources/Info.plist" ++ ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=Apple/tvOS/Resources ++ ++ ac_config_files="$ac_config_files Apple/tvOS/Resources/Info.plist" ++ ++ ;; ++ visionOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=Apple/visionOS/Resources ++ ++ ac_config_files="$ac_config_files Apple/visionOS/Resources/Info.plist" ++ ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=Apple/watchOS/Resources + +- ac_config_files="$ac_config_files iOS/Resources/Info.plist" ++ ac_config_files="$ac_config_files Apple/watchOS/Resources/Info.plist" + + ;; + *) +@@ -4485,6 +4593,9 @@ + 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 ;; ++ visionOS) as_fn_error $? "visionOS 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 +4650,8 @@ + case "$withval" in + yes) + case $ac_sys_system in +- Darwin|iOS) +- # iOS is able to share the macOS patch ++ Darwin|iOS|tvOS|visionOS|watchOS) ++ # iOS/tvOS/visionOS/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 +4669,8 @@ + 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|visionOS|watchOS) ++ # Always apply the compliance patch on iOS/tvOS/visionOS/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; } +@@ -4577,6 +4688,8 @@ + + + ++EXPORT_XROS_DEPLOYMENT_TARGET='#' ++ + + if test "$cross_compiling" = yes; then + case "$host" in +@@ -4614,6 +4727,78 @@ + ;; + 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-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 ++ ;; ++ *-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 +4889,15 @@ + 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/visionOS/watchOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; ++ tvOS/*) ++ define_xopen_source=no;; ++ visionOS/*) ++ 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 +4960,14 @@ + 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 / ++# XROS_DEPLOYMENT_TARGET / WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. ++ ++ ++ ++ ++ ++# XROS_DEPLOYMENT_TARGET should get exported + + + # checks for alternative programs +@@ -4810,6 +5008,16 @@ + 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 +7387,12 @@ + MULTIARCH="" ;; #( + iOS) : + MULTIARCH="" ;; #( ++ tvOS) : ++ MULTIARCH="" ;; #( ++ visionOS) : ++ MULTIARCH="" ;; #( ++ watchOS) : ++ MULTIARCH="" ;; #( + FreeBSD*) : + MULTIARCH="" ;; #( + *) : +@@ -7199,7 +7413,7 @@ + printf "%s\n" "$MULTIARCH" >&6; } + + case $ac_sys_system in #( +- iOS) : ++ iOS|tvOS|visionOS|watchOS) : + SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2` ;; #( + *) : + SOABI_PLATFORM=$PLATFORM_TRIPLET +@@ -7250,6 +7464,18 @@ + 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-xros*-simulator/clang) : ++ PY_SUPPORT_TIER=3 ;; #( ++ aarch64-apple-xros*/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) : +@@ -7688,7 +7914,7 @@ + case $ac_sys_system in + Darwin) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; +- iOS) ++ iOS|tvOS|visionOS|watchOS) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + *) + as_fn_error $? "Unknown platform for framework build" "$LINENO" 5;; +@@ -7754,7 +7980,7 @@ + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} + ;; +- iOS) ++ iOS|tvOS|visionOS|watchOS) + LDLIBRARY='libpython$(LDVERSION).dylib' + ;; + AIX*) +@@ -13570,7 +13796,7 @@ + BLDSHARED="$LDSHARED" + fi + ;; +- iOS/*) ++ iOS/*|tvOS/*|visionOS/*|watchOS/*) + LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + BLDSHARED="$LDSHARED" +@@ -13703,7 +13929,7 @@ + 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/*|visionOS/*|watchOS/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" + + # Issue #18075: the default maximum stack size (8MBytes) is too +@@ -13727,7 +13953,7 @@ + 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" = "visionOS" -o "$ac_sys_system" = "watchOS"; then + LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' + fi + ;; +@@ -15504,7 +15730,7 @@ + + ctypes_malloc_closure=yes + ;; #( +- iOS) : ++ iOS|tvOS|visionOS|watchOS) : + + ctypes_malloc_closure=yes + ;; #( +@@ -19256,12 +19482,6 @@ + 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 +@@ -19322,18 +19542,6 @@ + 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 +@@ -19766,24 +19974,6 @@ + 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 +@@ -20102,12 +20292,6 @@ + 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 +@@ -20376,11 +20560,11 @@ + + fi + +-# iOS defines some system methods that can be linked (so they are ++# iOS/tvOS/visionOS/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" != "visionOS" -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 : +@@ -20402,6 +20586,53 @@ + + 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} +@@ -23873,7 +24104,8 @@ + + + # 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 : +@@ -23987,7 +24219,7 @@ + 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 : +@@ -24170,6 +24402,7 @@ + fi + + done ++fi + + # check for long file support functions + ac_fn_c_check_func "$LINENO" "fseek64" "ac_cv_func_fseek64" +@@ -24435,10 +24668,10 @@ + + done + +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS, visionOS, 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" != "visionOS" -a "$ac_sys_system" != "watchOS" + then + + for ac_func in clock_settime +@@ -24755,7 +24988,7 @@ + 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" = "visionOS" || test "$ac_sys_system" = "watchOS"; then + ac_cv_buggy_getaddrinfo="no" + elif test "${enable_ipv6+set}" = set; then + ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" +@@ -26777,8 +27010,8 @@ + 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 = "visionOS" -o $ac_sys_system = "watchOS"; then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" + fi + +@@ -29648,7 +29881,7 @@ + { 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" = "visionOS" -o "$ac_sys_system" = "watchOS" ; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + else +@@ -30155,7 +30388,7 @@ + with_ensurepip=no ;; #( + WASI) : + with_ensurepip=no ;; #( +- iOS) : ++ iOS|tvOS|visionOS|watchOS) : + with_ensurepip=no ;; #( + *) : + with_ensurepip=upgrade +@@ -31105,6 +31338,9 @@ + NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 + Darwin) _PYTHREAD_NAME_MAXLEN=63;; + iOS) _PYTHREAD_NAME_MAXLEN=63;; ++ tvOS) _PYTHREAD_NAME_MAXLEN=63;; ++ visionOS) _PYTHREAD_NAME_MAXLEN=63;; ++ watchOS) _PYTHREAD_NAME_MAXLEN=63;; + FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 + OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 + *) _PYTHREAD_NAME_MAXLEN=;; +@@ -31136,7 +31372,7 @@ + ;; #( + Darwin) : + ;; #( +- iOS) : ++ iOS|tvOS|visionOS|watchOS) : + + + +@@ -35303,7 +35539,10 @@ + "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" ;; +- "iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES iOS/Resources/Info.plist" ;; ++ "Apple/iOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES Apple/iOS/Resources/Info.plist" ;; ++ "Apple/tvOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES Apple/tvOS/Resources/Info.plist" ;; ++ "Apple/visionOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES Apple/visionOS/Resources/Info.plist" ;; ++ "Apple/watchOS/Resources/Info.plist") CONFIG_FILES="$CONFIG_FILES Apple/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 42d94776cc7..4cf79576a55 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -330,6 +330,15 @@ + *-apple-ios*) + ac_sys_system=iOS + ;; ++ *-apple-tvos*) ++ ac_sys_system=tvOS ++ ;; ++ *-apple-xros*) ++ ac_sys_system=visionOS ++ ;; ++ *-apple-watchos*) ++ ac_sys_system=watchOS ++ ;; + *-*-darwin*) + ac_sys_system=Darwin + ;; +@@ -405,7 +414,7 @@ + # 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/visionOS/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 +429,17 @@ + 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-xros*-simulator) AR=arm64-apple-xros-simulator-ar ;; ++ aarch64-apple-xros*) AR=arm64-apple-xros-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 +448,17 @@ + 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-xros*-simulator) CC=arm64-apple-xros-simulator-clang ;; ++ aarch64-apple-xros*) CC=arm64-apple-xros-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 +467,17 @@ + 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-xros*-simulator) CPP=arm64-apple-xros-simulator-cpp ;; ++ aarch64-apple-xros*) CPP=arm64-apple-xros-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 +486,17 @@ + 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-xros*-simulator) CXX=arm64-apple-xros-simulator-clang++ ;; ++ aarch64-apple-xros*) CXX=arm64-apple-xros-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 +611,11 @@ + case $enableval in + yes) + case $ac_sys_system in +- Darwin) enableval=/Library/Frameworks ;; +- iOS) enableval=iOS/Frameworks/\$\(MULTIARCH\) ;; ++ Darwin) enableval=/Library/Frameworks ;; ++ iOS) enableval=Apple/iOS/Frameworks/\$\(MULTIARCH\) ;; ++ tvOS) enableval=Apple/tvOS/Frameworks/\$\(MULTIARCH\) ;; ++ visionOS) enableval=Apple/visionOS/Frameworks/\$\(MULTIARCH\) ;; ++ watchOS) enableval=Apple/watchOS/Frameworks/\$\(MULTIARCH\) ;; + *) AC_MSG_ERROR([Unknown platform for framework build]) + esac + esac +@@ -568,6 +624,9 @@ + 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]) ;; ++ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;; ++ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -666,9 +725,51 @@ + + prefix=$PYTHONFRAMEWORKPREFIX + PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" +- RESSRCDIR=iOS/Resources ++ RESSRCDIR=Apple/iOS/Resources + +- AC_CONFIG_FILES([iOS/Resources/Info.plist]) ++ AC_CONFIG_FILES([Apple/iOS/Resources/Info.plist]) ++ ;; ++ tvOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=Apple/tvOS/Resources ++ ++ AC_CONFIG_FILES([Apple/tvOS/Resources/Info.plist]) ++ ;; ++ visionOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=Apple/visionOS/Resources ++ ++ AC_CONFIG_FILES([Apple/visionOS/Resources/Info.plist]) ++ ;; ++ watchOS) : ++ FRAMEWORKINSTALLFIRST="frameworkinstallunversionedstructure" ++ FRAMEWORKALTINSTALLFIRST="frameworkinstallunversionedstructure " ++ FRAMEWORKINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKALTINSTALLLAST="frameworkinstallmobileheaders" ++ FRAMEWORKPYTHONW= ++ INSTALLTARGETS="libinstall inclinstall sharedinstall" ++ ++ prefix=$PYTHONFRAMEWORKPREFIX ++ PYTHONFRAMEWORKINSTALLNAMEPREFIX="@rpath/$PYTHONFRAMEWORKDIR" ++ RESSRCDIR=Apple/watchOS/Resources ++ ++ AC_CONFIG_FILES([Apple/watchOS/Resources/Info.plist]) + ;; + *) + AC_MSG_ERROR([Unknown platform for framework build]) +@@ -678,6 +779,9 @@ + ],[ + 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]) ;; ++ visionOS) AC_MSG_ERROR([visionOS builds must use --enable-framework]) ;; ++ watchOS) AC_MSG_ERROR([watchOS builds must use --enable-framework]) ;; + *) + PYTHONFRAMEWORK= + PYTHONFRAMEWORKDIR=no-framework +@@ -730,8 +834,8 @@ + case "$withval" in + yes) + case $ac_sys_system in +- Darwin|iOS) +- # iOS is able to share the macOS patch ++ Darwin|iOS|tvOS|visionOS|watchOS) ++ # iOS/tvOS/visionOS/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 +849,8 @@ + esac + ],[ + case $ac_sys_system in +- iOS) +- # Always apply the compliance patch on iOS; we can use the macOS patch ++ iOS|tvOS|visionOS|watchOS) ++ # Always apply the compliance patch on iOS/tvOS/visionOS/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]) + ;; +@@ -759,6 +863,8 @@ + ]) + AC_SUBST([APP_STORE_COMPLIANCE_PATCH]) + ++EXPORT_XROS_DEPLOYMENT_TARGET='#' ++ + AC_SUBST([_PYTHON_HOST_PLATFORM]) + if test "$cross_compiling" = yes; then + case "$host" in +@@ -794,6 +900,70 @@ + ;; + 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-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 ++ ;; ++ *-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 +1053,15 @@ + 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/visionOS/watchOS, defining _POSIX_C_SOURCE also disables platform specific features. + iOS/*) + define_xopen_source=no;; ++ tvOS/*) ++ define_xopen_source=no;; ++ visionOS/*) ++ 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 +1120,15 @@ + 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 / ++# XROS_DEPLOYMENT_TARGET / WATCHOS_DEPLOYMENT_TARGET enforced by the selected host triple. + AC_SUBST([IPHONEOS_DEPLOYMENT_TARGET]) ++AC_SUBST([TVOS_DEPLOYMENT_TARGET]) ++AC_SUBST([XROS_DEPLOYMENT_TARGET]) ++AC_SUBST([WATCHOS_DEPLOYMENT_TARGET]) ++ ++# XROS_DEPLOYMENT_TARGET should get exported ++AC_SUBST([EXPORT_XROS_DEPLOYMENT_TARGET]) + + # checks for alternative programs + +@@ -979,11 +1162,19 @@ + ], + ) + +-dnl Add the compiler flag for the iOS minimum supported OS version. ++dnl Add the compiler flag for the iOS/tvOS/watchOS minimum supported OS ++dnl version. visionOS doesn't use an explicit -mxros-version-min option - ++dnl it encodes the min version into the target triple. + AS_CASE([$ac_sys_system], + [iOS], [ + AS_VAR_APPEND([CFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) + AS_VAR_APPEND([LDFLAGS], [" -mios-version-min=${IPHONEOS_DEPLOYMENT_TARGET}"]) ++ ],[tvOS], [ ++ AS_VAR_APPEND([CFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mtvos-version-min=${TVOS_DEPLOYMENT_TARGET}"]) ++ ],[watchOS], [ ++ AS_VAR_APPEND([CFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) ++ AS_VAR_APPEND([LDFLAGS], [" -mwatchos-version-min=${WATCHOS_DEPLOYMENT_TARGET}"]) + ], + ) + +@@ -1172,6 +1363,9 @@ + AS_CASE([$ac_sys_system], + [Darwin*], [MULTIARCH=""], + [iOS], [MULTIARCH=""], ++ [tvOS], [MULTIARCH=""], ++ [visionOS], [MULTIARCH=""], ++ [watchOS], [MULTIARCH=""], + [FreeBSD*], [MULTIARCH=""], + [MULTIARCH=$($CC --print-multiarch 2>/dev/null)] + ) +@@ -1193,7 +1387,7 @@ + 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|visionOS|watchOS], [SOABI_PLATFORM=`echo "$PLATFORM_TRIPLET" | cut -d '-' -f2`], + [SOABI_PLATFORM=$PLATFORM_TRIPLET] + ) + +@@ -1220,16 +1414,22 @@ + [wasm32-unknown-wasip1/clang], [PY_SUPPORT_TIER=2], dnl WebAssembly System Interface preview1, clang + [x86_64-*-linux-gnu/clang], [PY_SUPPORT_TIER=2], dnl Linux on AMD64, any vendor, glibc, clang + +- [aarch64-pc-windows-msvc/msvc], [PY_SUPPORT_TIER=3], dnl Windows ARM64, MSVC +- [armv7l-*-linux-gnueabihf/gcc], [PY_SUPPORT_TIER=3], dnl ARMv7 LE with hardware floats, any vendor, glibc, gcc +- [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*-simulator/clang], [PY_SUPPORT_TIER=3], dnl iOS Simulator on arm64 +- [aarch64-apple-ios*/clang], [PY_SUPPORT_TIER=3], dnl iOS 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 +- [wasm32-*-emscripten/emcc], [PY_SUPPORT_TIER=3], dnl Emscripten ++ [aarch64-pc-windows-msvc/msvc], [PY_SUPPORT_TIER=3], dnl Windows ARM64, MSVC ++ [armv7l-*-linux-gnueabihf/gcc], [PY_SUPPORT_TIER=3], dnl ARMv7 LE with hardware floats, any vendor, glibc, gcc ++ [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*-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-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-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 ++ [wasm32-*-emscripten/emcc], [PY_SUPPORT_TIER=3], dnl Emscripten + + [PY_SUPPORT_TIER=0] + ) +@@ -1537,7 +1737,7 @@ + case $ac_sys_system in + Darwin) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)';; +- iOS) ++ iOS|tvOS|visionOS|watchOS) + LDLIBRARY='$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)';; + *) + AC_MSG_ERROR([Unknown platform for framework build]);; +@@ -1602,7 +1802,7 @@ + BLDLIBRARY='-L. -lpython$(LDVERSION)' + RUNSHARED=DYLD_LIBRARY_PATH=`pwd`${DYLD_LIBRARY_PATH:+:${DYLD_LIBRARY_PATH}} + ;; +- iOS) ++ iOS|tvOS|visionOS|watchOS) + LDLIBRARY='libpython$(LDVERSION).dylib' + ;; + AIX*) +@@ -3469,7 +3669,7 @@ + BLDSHARED="$LDSHARED" + fi + ;; +- iOS/*) ++ iOS/*|tvOS/*|visionOS/*|watchOS/*) + LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' + BLDSHARED="$LDSHARED" +@@ -3593,7 +3793,7 @@ + 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/*|visionOS/*|watchOS/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" + + # Issue #18075: the default maximum stack size (8MBytes) is too +@@ -3617,7 +3817,7 @@ + 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" = "visionOS" -o "$ac_sys_system" = "watchOS"; then + LINKFORSHARED="-Wl,-stack_size,$stack_size $LINKFORSHARED "'$(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK)' + fi + ;; +@@ -4105,7 +4305,7 @@ + dnl when do we need USING_APPLE_OS_LIBFFI? + ctypes_malloc_closure=yes + ], +- [iOS], [ ++ [iOS|tvOS|visionOS|watchOS], [ + ctypes_malloc_closure=yes + ], + [sunos5], [AS_VAR_APPEND([LIBFFI_LIBS], [" -mimpure-text"])] +@@ -5214,9 +5414,9 @@ + # 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 getlogin_r \ + getpeername getpgid getpid getppid getpriority _getpty \ +@@ -5224,8 +5424,7 @@ + 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 +@@ -5235,7 +5434,7 @@ + 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 \ +@@ -5250,12 +5449,20 @@ + AC_CHECK_FUNCS([lchmod]) + fi + +-# iOS defines some system methods that can be linked (so they are ++# iOS/tvOS/visionOS/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" != "visionOS" -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], +@@ -5550,20 +5757,22 @@ + [@%:@include ]) + + # 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]) +@@ -5602,10 +5811,10 @@ + ]) + ]) + +-# On Android and iOS, clock_settime can be linked (so it is found by ++# On Android, iOS, tvOS, visionOS, 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" != "visionOS" -a "$ac_sys_system" != "watchOS" + then + AC_CHECK_FUNCS([clock_settime], [], [ + AC_CHECK_LIB([rt], [clock_settime], [ +@@ -5763,7 +5972,7 @@ + [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" = "visionOS" || test "$ac_sys_system" = "watchOS"; then + ac_cv_buggy_getaddrinfo="no" + elif test "${enable_ipv6+set}" = set; then + ac_cv_buggy_getaddrinfo="no -- configured with --(en|dis)able-ipv6" +@@ -6356,8 +6565,8 @@ + 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 = "visionOS" -o $ac_sys_system = "watchOS"; then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(PYTHONFRAMEWORKDIR)/\$(PYTHONFRAMEWORK)" + fi + +@@ -7016,7 +7225,7 @@ + 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" = "visionOS" -o "$ac_sys_system" = "watchOS" ; then + ac_cv_file__dev_ptmx=no + ac_cv_file__dev_ptc=no + else +@@ -7316,7 +7525,7 @@ + AS_CASE([$ac_sys_system], + [Emscripten], [with_ensurepip=no], + [WASI], [with_ensurepip=no], +- [iOS], [with_ensurepip=no], ++ [iOS|tvOS|visionOS|watchOS], [with_ensurepip=no], + [with_ensurepip=upgrade] + ) + ]) +@@ -7704,6 +7913,9 @@ + NetBSD*) _PYTHREAD_NAME_MAXLEN=15;; # gh-131268 + Darwin) _PYTHREAD_NAME_MAXLEN=63;; + iOS) _PYTHREAD_NAME_MAXLEN=63;; ++ tvOS) _PYTHREAD_NAME_MAXLEN=63;; ++ visionOS) _PYTHREAD_NAME_MAXLEN=63;; ++ watchOS) _PYTHREAD_NAME_MAXLEN=63;; + FreeBSD*) _PYTHREAD_NAME_MAXLEN=19;; # gh-131268 + OpenBSD*) _PYTHREAD_NAME_MAXLEN=23;; # gh-131268 + *) _PYTHREAD_NAME_MAXLEN=;; +@@ -7728,7 +7940,7 @@ + [VxWorks*], [PY_STDLIB_MOD_SET_NA([_scproxy], [termios], [grp])], + dnl The _scproxy module is available on macOS + [Darwin], [], +- [iOS], [ ++ [iOS|tvOS|visionOS|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